-
Notifications
You must be signed in to change notification settings - Fork 74
Trusted Types for function constructor
Krzysztof Kotowicz edited this page Jun 16, 2020
·
1 revision
In Chrome's Trusted Types implementation there's a bug, that throws upon calling a Function
constructor, even if all its arguments are TrustedScript
instances:
<meta http-equiv="content-security-policy" content="require-trusted-types-for 'script';">
<body>
<script>
let opaqueScriptPolicy =
trustedTypes.createPolicy('test', {
createScript: opaqueScript => {
return opaqueScript;
},
});
const expr = 'alert(1)';
const opaqueExpr = opaqueScriptPolicy.createScript(expr);
new Function(opaqueExpr)(); // This throws
</script>
</body>
We have developed a temporary workaround - a replacement Function constructor that will create a new function instance via using eval
(using a TrustedScript
value).
class TrustedFunction {
static policy = trustedTypes.createPolicy('TrustedFunctionWorkaround', {
createScript: (_, ...args) => {
args.forEach( (arg) => {
if (!trustedTypes.isScript(arg)) {
throw new Error("TrustedScripts only, please");
}
});
// NOTE: This is insecure without parsing the arguments and body,
// Malicious inputs can escape the function body and execute immediately!
const fnArgs = args.slice(0, -1).join(',');
const fnBody = args.pop().toString();
const body = `(function anonymous(
${fnArgs}
) {
${fnBody}
})`;
return body;
}
});
constructor(...args) {
return (window || self).eval(TrustedFunction.policy.createScript('', ...args));
}
}
To use it, simply call new TrustedFunction
instead of new Function
. Note that the input to the function constructor must be trusted, as otherwise the attacker might execute the code at the moment you call the TrustedFunction
constructor!