From 980f6a4958ee3df6007d716a7c32ea7c64700b51 Mon Sep 17 00:00:00 2001 From: Bjarki Date: Fri, 12 Feb 2021 00:16:12 +0000 Subject: [PATCH] fix(core): address Trusted Types bug in Chrome 83 (#40815) In Chrome 83 passing a TrustedScript to eval just returns the TrustedScript back without evaluating it, causing the newTrustedFunctionFor{Dev,JIT} functions to fail. This is a browser bug that has been fixed in Chrome 84, and only affects Angular applications running with JIT (which includes unit tests). As a temporary workaround for users still on Chrome 83, detect when this occurs in the newTrustedFunctionFor* functions and fall back to the straightforward, non-Trusted Types compatible implementation. The only combination that is left affected consists of Angular applications running with JIT, that have explicitly configured Trusted Types in enforcement mode, with users that are still on Chrome 83. Also correct docstring for newTrustedFunctionForJIT. PR Close #40815 --- .../compiler/src/output/output_jit_trusted_types.ts | 13 +++++++++---- packages/core/src/util/security/trusted_types.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/compiler/src/output/output_jit_trusted_types.ts b/packages/compiler/src/output/output_jit_trusted_types.ts index 8d831e592a..b7e0fea059 100644 --- a/packages/compiler/src/output/output_jit_trusted_types.ts +++ b/packages/compiler/src/output/output_jit_trusted_types.ts @@ -93,9 +93,7 @@ function trustedScriptFromString(script: string): TrustedScript|string { } /** - * Unsafely call the Function constructor with the given string arguments. It - * is only available in development mode, and should be stripped out of - * production code. + * Unsafely call the Function constructor with the given string arguments. * @security This is a security-sensitive function; any use of this function * must go through security review. In particular, it must be assured that it * is only called from the JIT compiler, as use in other code can lead to XSS @@ -113,7 +111,7 @@ export function newTrustedFunctionForJIT(...args: string[]): Function { // below, where the Chromium bug is also referenced: // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor const fnArgs = args.slice(0, -1).join(','); - const fnBody = args.pop()!.toString(); + const fnBody = args[args.length - 1]; const body = `(function anonymous(${fnArgs} ) { ${fnBody} })`; @@ -122,6 +120,13 @@ export function newTrustedFunctionForJIT(...args: string[]): Function { // being stripped out of JS binaries even if not used. The global['eval'] // indirection fixes that. const fn = global['eval'](trustedScriptFromString(body) as string) as Function; + if (fn.bind === undefined) { + // Workaround for a browser bug that only exists in Chrome 83, where passing + // a TrustedScript to eval just returns the TrustedScript back without + // evaluating it. In that case, fall back to the most straightforward + // implementation: + return new Function(...args); + } // To completely mimic the behavior of calling "new Function", two more // things need to happen: diff --git a/packages/core/src/util/security/trusted_types.ts b/packages/core/src/util/security/trusted_types.ts index 59c45e4662..75f7d7320f 100644 --- a/packages/core/src/util/security/trusted_types.ts +++ b/packages/core/src/util/security/trusted_types.ts @@ -111,7 +111,7 @@ export function newTrustedFunctionForDev(...args: string[]): Function { // below, where the Chromium bug is also referenced: // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor const fnArgs = args.slice(0, -1).join(','); - const fnBody = args.pop()!.toString(); + const fnBody = args[args.length - 1]; const body = `(function anonymous(${fnArgs} ) { ${fnBody} })`; @@ -120,6 +120,13 @@ export function newTrustedFunctionForDev(...args: string[]): Function { // being stripped out of JS binaries even if not used. The global['eval'] // indirection fixes that. const fn = global['eval'](trustedScriptFromString(body) as string) as Function; + if (fn.bind === undefined) { + // Workaround for a browser bug that only exists in Chrome 83, where passing + // a TrustedScript to eval just returns the TrustedScript back without + // evaluating it. In that case, fall back to the most straightforward + // implementation: + return new Function(...args); + } // To completely mimic the behavior of calling "new Function", two more // things need to happen: