-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
Copy pathblock-text-node-insertion-into-script-element.html
208 lines (185 loc) · 7.73 KB
/
block-text-node-insertion-into-script-element.html
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
<!DOCTYPE html>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/block-text-node-insertion.js"></script>
<script src="support/csp-violations.js"></script>
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';">
<meta http-equiv="Content-Security-Policy" content="connect-src 'none'">
</head>
<body>
<div id="container"></div>
<script id="script1">"hello world!";</script>
<script>
var container = document.querySelector("#container");
const policy_dict = {
createScript: s => (s.includes("fail") ? null : s.replace("default", "count")),
createHTML: s => s.replace(/2/g, "3"),
};
const policy = trustedTypes.createPolicy("policy", policy_dict);
let seenSinks;
function clearSeenSinks() { seenSinks = []; }
clearSeenSinks();
const policy_dict_default = {
createScript: (s, _, sink) => {
seenSinks.push(sink);
return (s.includes("fail") ? null : s.replace("default", "count"));
},
createHTML: (s, _, sink) => {
seenSinks.push(sink);
return s.replace(/2/g, "3");
},
};
function createScriptElement() { return document.createElement("script"); }
// Original report:
promise_test(async t => {
// Setup: Create a <script> element with a <p> child.
let s = createScriptElement();
// Sanity check: Element child content (<p>) doesn't count as source text.
let p = document.createElement("p");
p.text = "hello('world');";
s.appendChild(p);
container.appendChild(s);
assert_equals(s.text, "");
// Try to insertAdjacentText into the <script>, starting from the <p>
await checkMessage(_ =>
p.insertAdjacentText("beforebegin",
"postMessage('beforebegin should be blocked', '*');")
);
assert_true(s.text.includes("postMessage"));
await checkMessage(_ =>
p.insertAdjacentText("afterend",
"postMessage('afterend should be blocked', '*');")
);
assert_true(s.text.includes("after"));
}, "Regression test: Bypass via insertAdjacentText, initial comment.");
// Like the "Original Report" test case above, but avoids use of the "text"
// accessor that <svg:script> doesn't support.
promise_test(async t => {
let s = createScriptElement();
// Sanity check: Element child content (<p>) doesn't count as source text.
let p = document.createElement("p");
p.textContent = "hello('world');";
s.appendChild(p);
container.appendChild(s);
// Try to insertAdjacentText into the <script>, starting from the <p>
await checkMessage(_ =>
p.insertAdjacentText("beforebegin",
"postMessage('beforebegin should be blocked', '*');")
);
assert_true(s.textContent.includes("postMessage"));
await checkMessage(_ =>
p.insertAdjacentText("afterend",
"postMessage('afterend should be blocked', '*');")
);
assert_true(s.textContent.includes("after"));
}, "Regression test: Bypass via insertAdjacentText, textContent.");
// Variant: Create a <script> element and create & insert a text node. Then
// insert it into the document container to make it live.
promise_test(async t => {
// Setup: Create a <script> element, and insert text via a text node.
let s = createScriptElement();
let text = document.createTextNode("postMessage('appendChild with a " +
"text node should be blocked', '*');");
s.appendChild(text);
await checkMessage(_ => container.appendChild(s));
}, "Regression test: Bypass via appendChild into off-document script element.");
// Variant: Create a <script> element and insert it into the document. Then
// create a text node and insert it into the live <script> element.
promise_test(async t => {
// Setup: Create a <script> element, insert it into the doc, and then create
// and insert text via a text node.
let s = createScriptElement();
container.appendChild(s);
let text = document.createTextNode("postMessage('appendChild with a live " +
"text node should be blocked', '*');");
await checkMessage(_ => s.appendChild(text));
}, "Regression test: Bypass via appendChild into live script element.");
promise_test(async t => {
const inserted_script = document.getElementById("script1");
await trusted_type_violation_for(TypeError, _ =>
inserted_script.innerHTML = "2+2"
);
let new_script = createScriptElement();
await trusted_type_violation_for(TypeError, _ =>
new_script.innerHTML = "2+2"
);
container.appendChild(new_script);
const trusted_html = policy.createHTML("2*4");
assert_equals("" + trusted_html, "3*4");
await no_trusted_type_violation_for(_ =>
inserted_script.innerHTML = trusted_html
);
assert_equals(inserted_script.textContent, "3*4");
new_script = createScriptElement();
new_script.innerHTML = trusted_html;
await trusted_type_violation_without_exception_for(_ =>
container.appendChild(new_script)
);
assert_equals(inserted_script.textContent, "3*4");
}, "Spot tests around script + innerHTML interaction.");
// Create default policy. Wrapped in a promise_test to ensure it runs only
// after the other tests.
let default_policy;
promise_test(t => {
default_policy = trustedTypes.createPolicy("default", policy_dict_default);
return Promise.resolve();
}, "Prep for subsequent tests: Create default policy.");
promise_test(async t => {
t.add_cleanup(clearSeenSinks);
let s = createScriptElement();
let text = document.createTextNode("postMessage('default', '*');");
s.appendChild(text);
await no_trusted_type_violation_for(async _ =>
await checkMessage(_ => container.appendChild(s), 1)
);
assert_array_equals(seenSinks, ["HTMLScriptElement text"]);
}, "Test that default policy applies.");
promise_test(async t => {
t.add_cleanup(clearSeenSinks);
let s = createScriptElement();
let text = document.createTextNode("fail");
s.appendChild(text);
await trusted_type_violation_without_exception_for(async _ =>
await checkMessage(_ => container.appendChild(s), 0)
);
assert_array_equals(seenSinks, ["HTMLScriptElement text"]);
}, "Test a failing default policy.");
promise_test(async t => {
t.add_cleanup(clearSeenSinks);
const inserted_script = document.getElementById("script1");
await no_trusted_type_violation_for(_ =>
inserted_script.innerHTML = "2+2"
);
assert_array_equals(seenSinks, ["Element innerHTML"]);
clearSeenSinks();
assert_equals(inserted_script.textContent, "3+3");
let new_script = createScriptElement();
await no_trusted_type_violation_for(_ => {
new_script.innerHTML = "2+2";
container.appendChild(new_script);
});
assert_array_equals(seenSinks, ["Element innerHTML", "HTMLScriptElement text"]);
clearSeenSinks();
assert_equals(inserted_script.textContent, "3+3");
const trusted_html = default_policy.createHTML("2*4");
assert_equals("" + trusted_html, "3*4");
await no_trusted_type_violation_for(_ =>
inserted_script.innerHTML = trusted_html
);
assert_array_equals(seenSinks, [undefined]);
clearSeenSinks();
assert_equals(inserted_script.textContent, "3*4");
new_script = createScriptElement();
await no_trusted_type_violation_for(_ => {
new_script.innerHTML = trusted_html;
container.appendChild(new_script);
});
assert_array_equals(seenSinks, ["HTMLScriptElement text"]);
clearSeenSinks();
assert_equals(inserted_script.textContent, "3*4");
}, "Spot tests around script + innerHTML interaction with default policy.");
</script>
</body>
</html>