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
This commit is contained in:
parent
9661873df1
commit
980f6a4958
|
@ -93,9 +93,7 @@ function trustedScriptFromString(script: string): TrustedScript|string {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsafely call the Function constructor with the given string arguments. It
|
* Unsafely call the Function constructor with the given string arguments.
|
||||||
* is only available in development mode, and should be stripped out of
|
|
||||||
* production code.
|
|
||||||
* @security This is a security-sensitive function; any use of this function
|
* @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
|
* 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
|
* 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:
|
// below, where the Chromium bug is also referenced:
|
||||||
// https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
// https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
||||||
const fnArgs = args.slice(0, -1).join(',');
|
const fnArgs = args.slice(0, -1).join(',');
|
||||||
const fnBody = args.pop()!.toString();
|
const fnBody = args[args.length - 1];
|
||||||
const body = `(function anonymous(${fnArgs}
|
const body = `(function anonymous(${fnArgs}
|
||||||
) { ${fnBody}
|
) { ${fnBody}
|
||||||
})`;
|
})`;
|
||||||
|
@ -122,6 +120,13 @@ export function newTrustedFunctionForJIT(...args: string[]): Function {
|
||||||
// being stripped out of JS binaries even if not used. The global['eval']
|
// being stripped out of JS binaries even if not used. The global['eval']
|
||||||
// indirection fixes that.
|
// indirection fixes that.
|
||||||
const fn = global['eval'](trustedScriptFromString(body) as string) as Function;
|
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
|
// To completely mimic the behavior of calling "new Function", two more
|
||||||
// things need to happen:
|
// things need to happen:
|
||||||
|
|
|
@ -111,7 +111,7 @@ export function newTrustedFunctionForDev(...args: string[]): Function {
|
||||||
// below, where the Chromium bug is also referenced:
|
// below, where the Chromium bug is also referenced:
|
||||||
// https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
// https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
||||||
const fnArgs = args.slice(0, -1).join(',');
|
const fnArgs = args.slice(0, -1).join(',');
|
||||||
const fnBody = args.pop()!.toString();
|
const fnBody = args[args.length - 1];
|
||||||
const body = `(function anonymous(${fnArgs}
|
const body = `(function anonymous(${fnArgs}
|
||||||
) { ${fnBody}
|
) { ${fnBody}
|
||||||
})`;
|
})`;
|
||||||
|
@ -120,6 +120,13 @@ export function newTrustedFunctionForDev(...args: string[]): Function {
|
||||||
// being stripped out of JS binaries even if not used. The global['eval']
|
// being stripped out of JS binaries even if not used. The global['eval']
|
||||||
// indirection fixes that.
|
// indirection fixes that.
|
||||||
const fn = global['eval'](trustedScriptFromString(body) as string) as Function;
|
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
|
// To completely mimic the behavior of calling "new Function", two more
|
||||||
// things need to happen:
|
// things need to happen:
|
||||||
|
|
Loading…
Reference in New Issue