feat(core): add Trusted Types workaround for Function constructor (#39209)
Chrome currently does not support passing TrustedScript to the Function constructor, and instead fails with a Trusted Types violation when called. As the Function constructor is used in a handful of places within Angular, such as in the JIT compiler and named_array_type, the workaround proposed on the following page is implemented: https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor To be precise, it constructs a string representing an anonymous function in a way that is equivalent to what the Function constructor does, promotes it to a TrustedScript and then calls eval. To facilitate backwards compatibility, new Function is used directly in environments that do not support Trusted Types. PR Close #39209
This commit is contained in:
parent
929e0df377
commit
5913e5c4e8
|
@ -85,3 +85,49 @@ export function trustedScriptFromString(script: string): TrustedScript|string {
|
|||
export function trustedScriptURLFromString(url: string): TrustedScriptURL|string {
|
||||
return getPolicy()?.createScriptURL(url) || url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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 development code, as use in production code can lead to
|
||||
* XSS vulnerabilities.
|
||||
*/
|
||||
export function newTrustedFunctionForDev(...args: string[]): Function {
|
||||
if (typeof ngDevMode === 'undefined') {
|
||||
throw new Error('newTrustedFunctionForDev should never be called in production');
|
||||
}
|
||||
if (!global.trustedTypes) {
|
||||
// In environments that don't support Trusted Types, fall back to the most
|
||||
// straightforward implementation:
|
||||
return new Function(...args);
|
||||
}
|
||||
|
||||
// Chrome currently does not support passing TrustedScript to the Function
|
||||
// constructor. The following implements the workaround proposed on the page
|
||||
// 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 body = `(function anonymous(${fnArgs}
|
||||
) { ${fnBody}
|
||||
})`;
|
||||
|
||||
// Using eval directly confuses the compiler and prevents this module from
|
||||
// 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;
|
||||
|
||||
// To completely mimic the behavior of calling "new Function", two more
|
||||
// things need to happen:
|
||||
// 1. Stringifying the resulting function should return its source code
|
||||
fn.toString = () => body;
|
||||
// 2. When calling the resulting function, `this` should refer to `global`
|
||||
return fn.bind(global);
|
||||
|
||||
// When Trusted Types support in Function constructors is widely available,
|
||||
// the implementation of this function can be simplified to:
|
||||
// return new Function(...args.map(a => trustedScriptFromString(a)));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue