简体   繁体   English

Angular 中动态编译的延迟加载动态路由导致“不安全评估”错误

[英]Dynamically compiled lazy loaded dynamic routes in Angular causing 'unsafe-eval' error

In the index.html file of the angular application after applying the Content Security Policy, the application is giving 'unsafe-eval' console error as below -在应用内容安全策略后,在 angular 应用程序的 index.html 文件中,应用程序给出了“unsafe-eval”控制台错误,如下所示 -

core.js:4442 ERROR Error: Uncaught (in promise): EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "default-src 'self'".

EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "default-src 'self'".

    at new Function (<anonymous>)
    at JitEvaluator.evaluateCode (compiler.js:6740)
    at JitEvaluator.evaluateStatements (compiler.js:6714)
    at CompilerFacadeImpl.jitExpression (compiler.js:19300)
    at CompilerFacadeImpl.compileNgModule (compiler.js:19238)
    at Function.get (core.js:25864)
    at getNgModuleDef (core.js:1853)
    at new NgModuleFactory$1 (core.js:24270)
    at Compiler_compileModuleSync__POST_R3__ (core.js:27085)
    at Compiler_compileModuleAsync__POST_R3__ [as compileModuleAsync] (core.js:27090)
    at resolvePromise (zone-evergreen.js:798)
    at resolvePromise (zone-evergreen.js:750)
    at zone-evergreen.js:860
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:27483)
    at ZoneDelegate.invokeTask (zone-evergreen.js:398)
    at Zone.runTask (zone-evergreen.js:167)
    at drainMicroTaskQueue (zone-evergreen.js:569)

This error is getting caused by using the compileModuleAsync() method from Compiler class as I am trying to build the module dynamically.当我尝试动态构建模块时,此错误是由于使用Compiler类中的compileModuleAsync()方法引起的。

If I don't use the Content Security Policy, then the application works fine and it doesn't give such console error.如果我不使用内容安全策略,则应用程序运行良好,并且不会出现此类控制台错误。 Below is the policy details -以下是政策详情——

<meta http-equiv="Content-Security-Policy" content="default-src 'self';" />

As per the observation from callstack, the below function part of Angular Framework uses new Function() expression and leads to security issue -根据调用堆栈的观察,Angular Framework 的以下函数部分使用new Function()表达式并导致安全问题 -

 /**
     * Evaluate a piece of JIT generated code.
     * @param sourceUrl The URL of this generated code.
     * @param ctx A context object that contains an AST of the code to be evaluated.
     * @param vars A map containing the names and values of variables that the evaluated code might
     * reference.
     * @param createSourceMap If true then create a source-map for the generated code and include it
     * inline as a source-map comment.
     * @returns The result of evaluating the code.
     */
    evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
        let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
        const fnArgNames = [];
        const fnArgValues = [];
        for (const argName in vars) {
            fnArgValues.push(vars[argName]);
            fnArgNames.push(argName);
        }
        if (createSourceMap) {
            // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise
            // E.g. ```
            // function anonymous(a,b,c
            // /**/) { ... }```
            // We don't want to hard code this fact, so we auto detect it via an empty function first.
            const emptyFn = new Function(...fnArgNames.concat('return null;')).toString();
            const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
            fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
        }
        const fn = new Function(...fnArgNames.concat(fnBody));
        return this.executeFunction(fn, fnArgValues);
    }

This is the routes.json in which I am trying to build configuration written in the loadChildren -这是我试图构建在 loadChildren 中编写的配置的 routes.json -

{
      path: '',
      componentName: 'dummy',
      children: [
        {
          path: '',
          pathMatch: 'full',
          redirectTo: 'set-focus-action',
        },
        {
          path: 'set-focus-action',
          loadChildren: {
            routes: [
              {
                path: '',
                componentName: 'dynamicType1',
              },
            ],
          },
        },
      ],
    }

Below is the code to build the module -以下是构建模块的代码 -

private featureModule(loadChildren: string): Observable<Type<any>> {
    return this.getRoutes(loadChildren).pipe(
      switchMap((routesConfig) => {
        const module = NgModule(this.createFeatureModule(routesConfig))(
          class {}
        );
        return from(this.compiler.compileModuleAsync(module));
      }),
      map((m) => {
        return m.moduleType;
      })
    );
  }

Also, I am using JitCompilerFactory for this compiler -另外,我正在为此编译器使用 JitCompilerFactory -

{ provide: COMPILER_OPTIONS, useValue: {}, multi: true },
        {
          provide: CompilerFactory,
          useClass: JitCompilerFactory,
          deps: [COMPILER_OPTIONS],
        },
        {
          provide: Compiler,
          useFactory: createCompiler,
          deps: [CompilerFactory],
        }

Please let me know in-case any other details.如果有任何其他细节,请告诉我。 Any suggestions would be really helpful.任何建议都会非常有帮助。

Below is a link for stackblitz where this issue is getting reproducible https://stackblitz.com/github/HimanshuGoel/unsafe-eval-issue?file=src%2Findex.html以下是 stackblitz 的链接,此问题正在重现https://stackblitz.com/github/HimanshuGoel/unsafe-eval-issue?file=src%2Findex.html

在此处输入图片说明

If I remove this CSP, it gets render correctly -如果我删除此 CSP,它会正确呈现 -

在此处输入图片说明

There is unfortunately no direct way around it.不幸的是,没有直接的方法可以解决它。 The angular JIT compiler needs to use new Function , and to generate a dynamic module, you need the JIT compiler. angular JIT 编译器需要使用new Function ,而要生成动态模块,则需要 JIT 编译器。

So you have two options, add unsafe-eval as content source:所以你有两个选择,添加unsafe-eval作为内容源:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-eval';" />

Or re-evaluate your need for a dynamic module by heading back to the drawing board.或者通过返回绘图板重新评估您对动态模块的需求。 In general it is advised to not use JIT at all, because of the size increase and speed reduction it brings.一般来说,建议根本不要使用 JIT,因为它带来的大小增加和速度降低。 For instance the newest angular versions uses AOT by default, even in ng serve mode.例如,最新的 angular 版本默认使用 AOT,即使在ng serve模式下也是如此。

It seems the reason for this issue is a current Angular deficiency似乎这个问题的原因是当前的 Angular 缺陷

This is a minimalistic reproduction of the issue.这是该问题的简约再现 All we need is to add the CSP meta tag to the page head on the standard stackblitz app:我们所需要的只是将 CSP 元标记添加到标准 stackblitz 应用程序的页面头部:

<meta http-equiv="Content-Security-Policy" content="default-src 'self';" />

Support for CSP would be provided by a Webpack configuration Webpack 配置将提供对 CSP 的支持

webpack is capable of adding nonce to all scripts that it loads webpack 能够将nonce添加到它加载的所有脚本中

however, this is not currently supported by angular :但是, angular 目前不支持此功能:

Ahead-of-Time (AOT) compilation (aka ng build --prod) separates out all JavaScript code from the index.html file.提前 (AOT) 编译(又名 ng build --prod)从 index.html 文件中分离出所有 JavaScript 代码。 Unfortunately, processing of the CSS is not as neat and styles remain inline in all the components (see this ticket for tracking).不幸的是,CSS 的处理并不那么整洁,并且样式在所有组件中都保持内联(请参阅此标签以进行跟踪)。 So, we have to put up with unpleasant style-src 'unsafe-inline'.所以,我们不得不忍受令人不快的 style-src 'unsafe-inline'。

As for the scripts, 'unsafe-inline' is also required if we want plugins to work.至于脚本,如果我们想让插件工作,还需要'unsafe-inline'。 There will be a way with angular/angular#26152 though: a combination of nonce-based CSP with strict-dynamic directive. angular/angular#26152会有一种方法:基于随机数的 CSP 与严格动态指令的组合。 Hence, if a script trusted with a nonce creates a new script at runtime, this new script will also be considered legitimate.因此,如果受随机数信任的脚本在运行时创建了一个新脚本,这个新脚本也将被认为是合法的。

so as per team Angular's recommendation the only current way to use the CSP header is with 'unsafe-inline' and do some refactoring (ie not using lazy loaded modules??? the horror...)因此,根据团队 Angular 的建议,当前使用 CSP 标头的唯一方法是使用'unsafe-inline'并进行一些重构(即不使用延迟加载模块???恐怖...)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Angular:拒绝将字符串评估为 JavaScript,因为“不安全评估” - Angular: Refused to evaluate a string as JavaScript because 'unsafe-eval' AOT-延迟加载的路由未编译 - AOT - Lazy loaded routes are not being compiled ExcelJS:Angular 应用程序中的“未捕获的 EvalError:'unsafe-eval' 不是以下内容中允许的脚本源” - ExcelJS: "Uncaught EvalError: 'unsafe-eval' is not an allowed source of script in the following Content" in Angular App Angular 5 &amp; CSP - 拒绝将字符串评估为 JavaScript,因为“unsafe-eval”不是允许的脚本源 - Angular 5 & CSP - Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script Angular 9 通用和延迟加载的路由 - Angular 9 Universal and lazy loaded routes Angular 8懒加载路线和守卫 - Angular 8 lazy loaded routes and guards 无法在延迟加载的模块中动态导航-Angular动态路由 - Not able to navigate dynamically inside a lazy loaded module - Angular Dynamic Routing 延迟加载模块中的 Angular single-spa 延迟加载路由调用未定义的 webpack 错误 - Angular single-spa lazy loaded routes in lazy loaded modules give call of undefined webpack error 动态修改延迟加载的路由不起作用 - Dynamically modifying lazy loaded routes not working 如何获取不安全评估兼容的Fabric JS和Lodash JS - How to get unsafe-eval compliant Fabric js and lodash js
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM