-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Expand file tree
/
Copy patheval-function-constructor-untrusted-arguments-and-applying-default-policy.html
More file actions
81 lines (77 loc) · 3.7 KB
/
Copy patheval-function-constructor-untrusted-arguments-and-applying-default-policy.html
File metadata and controls
81 lines (77 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html>
<head>
<link rel="help" href="https://w3c.github.io/webappsec-csp/#can-compile-strings">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'">
</head>
<body>
<script>
let policy = trustedTypes.createPolicy("p", { createScript: s => s });
// Define a default policy that rename variables Xn to Yn.
function renameVariableXtoY(s) { return s.replace(/X([1-9]+)/g, "Y$1"); }
trustedTypes.createPolicy("default", {
createScript: s => renameVariableXtoY(s)
});
// A plain string is still untrusted, so it still triggers the default
// policy's variable-renaming behavior.
const args = ["X1", "X2", "X3 = 5", "return (X1+X2)*X3;"];
// Wrap the function arguments into TrustedTypes, except for the one at the
// specified index. That argument will cause isTrusted=false in
// EnsureCSPDoesNotBlockStringCompilation and so "Get Trusted Type
// compliant string" will be executed on the function text, which use Xn
// variables. Consequently, the default policy will modify the function text
// which will cause an EvalError to be thrown.
args.forEach((_, index) => {
test(t => {
let mixed_args = args.map((arg_value, arg_index) =>
arg_index == index ? arg_value : policy.createScript(arg_value));
assert_throws_js(EvalError, _ => new Function(...mixed_args));
}, `plain string at index ${index} (default policy modifying the function text).`);
});
// Do the same but apply the variable renaming before building the function,
// so that the default policy won't modify the function text anymore. In
// that case, the function is built without error.
const argsWithY = args.map(renameVariableXtoY);
argsWithY.forEach((_, index) => {
test(t => {
let mixed_args = argsWithY.map((arg_value, arg_index) =>
arg_index == index ? arg_value : policy.createScript(arg_value));
const f = new Function(...mixed_args);
assert_equals(f(1,2,3), 9);
assert_equals(f(1,2), 15);
}, `plain string at index ${index} (default policy leaving the function text unchanged).`);
});
// A TrustedScript with a forged toString() is still genuinely trusted:
// trust is derived from HostGetCodeForEval reading the real internal data,
// not from ToString(). So isTrusted is always true here, the default
// policy is never invoked, and the function is always built from the
// real trusted text. The forged toString() values below are chosen to
// produce a different, observable result (a ReferenceError or a wrong
// number) if they were ever mistakenly used instead of the real trusted
// data, so a regression to the old toString()-based behavior would make
// this test fail. A call counter also verifies toString() is never invoked
// at all
const trustedArgs = ["a", "b", "c = 5", "return (a+b)*c;"];
const forgedArgs = ["a2", "b2", "c = 50", "return (a+b)*c+1;"];
trustedArgs.forEach((_, index) => {
test(t => {
let toStringCallCount = 0;
let mixed_args = trustedArgs.map((arg_value, arg_index) => {
let obj = policy.createScript(arg_value);
return arg_index == index ?
Object.assign(obj, { toString: () => {
toStringCallCount++;
return forgedArgs[arg_index];
}}) : obj;
});
const f = new Function(...mixed_args);
assert_equals(toStringCallCount, 0,
"toString() should never be called on a TrustedScript argument");
assert_equals(f(1,2,3), 9);
assert_equals(f(1,2), 15);
}, `TrustedScript with forged toString() at index ${index} (default policy not invoked).`);
});
</script>