Skip to content

Commit a11162a

Browse files
fred-wangmoz-wptsync-bot
authored andcommitted
Add more Trusted Types enforcement tests testing a source text transformed by the default policy.
This verifies that the source text transformed by the default policy is used for various steps of "prepare the script element": https://html.spec.whatwg.org/#prepare-the-script-element PR w3c/trusted-types#579 Differential Revision: https://phabricator.services.mozilla.com/D251456 bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1968383 gecko-commit: b40ba3e6cd668c9890ed7e4c6bdfdf2ee60cbcc4 gecko-reviewers: smaug
1 parent e3c896f commit a11162a

7 files changed

+426
-2
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="support/namespaces.js"></script>
5+
<script src="support/passthroughpolicy.js"></script>
6+
<script src="support/script-messages.js"></script>
7+
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#enforcement-in-scripts">
8+
<link rel="help" href="https://html.spec.whatwg.org/#prepare-the-script-element">
9+
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';">
10+
<!-- This test covers the following step from the "prepare the script element"
11+
algorithm, verifying that "source text" is the one after application of
12+
the default policy: "If el has no src attribute, and source text is the
13+
empty string, then return." -->
14+
<div id="htmlContainer">
15+
<script id="scriptToCreateNonEmptyHTMLScript" type="unknown">;</script>
16+
</div>
17+
<svg id="svgContainer">
18+
<script id="scriptToCreateNonEmptySVGScript" type="unknown">;</script>
19+
</svg>
20+
<script>
21+
// Define a default policy that transforms empty script string to some source
22+
// logging a RUN message and other script strings to empty.
23+
trustedTypes.createPolicy("default", {
24+
createScript: (value, _, sink) => {
25+
window.log_message("CREATE_SCRIPT");
26+
window.log_message(sink);
27+
return value.length ? "" : LOG_RUN_MESSAGE;
28+
}
29+
});
30+
31+
promise_test(async t => {
32+
let messages = await script_messages_for(_ => {
33+
// Current version of the specification requires the script text to change
34+
// in order to force a call to the default policy callback with sink
35+
// "HTMLScriptElement text". If the following PR is accepted, this could
36+
// be simplified to create_html_script_with_untrusted_source_text("").
37+
// https://github.com/w3c/trusted-types/pull/579
38+
let script = document.getElementById("scriptToCreateNonEmptyHTMLScript");
39+
script.remove();
40+
script.removeAttribute("type");
41+
script.firstChild.remove();
42+
htmlContainer.appendChild(script);
43+
});
44+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "RUN"]);
45+
}, "Empty HTMLScriptElement is executed if the default policy makes it non-empty.");
46+
47+
promise_test(async t => {
48+
let messages = await script_messages_for(_ => {
49+
let script = create_html_script_with_untrusted_source_text(LOG_RUN_MESSAGE);
50+
htmlContainer.appendChild(script);
51+
});
52+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text"]);
53+
54+
}, "Non-empty HTMLScriptElement is not executed if the default policy makes it empty.");
55+
56+
promise_test(async t => {
57+
let messages = await script_messages_for(_ => {
58+
// Note: Using create_html_script_with_untrusted_source_text("") may not
59+
// guarantee the script to be untrusted for implementations using a
60+
// script-based enforcement mechanism. So make sure we do modify the text.
61+
let script = document.getElementById("scriptToCreateNonEmptySVGScript");
62+
script.remove();
63+
script.removeAttribute("type");
64+
script.firstChild.remove();
65+
svgContainer.appendChild(script);
66+
});
67+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "RUN"]);
68+
}, "Empty SVGScriptElement is executed if the default policy makes it non-empty.");
69+
70+
promise_test(async t => {
71+
let messages = await script_messages_for(_ => {
72+
let script = create_svg_script_with_untrusted_source_text(LOG_RUN_MESSAGE);
73+
svgContainer.appendChild(script);
74+
});
75+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text"]);
76+
77+
}, "Non-empty SVGScriptElement is not executed if the default policy makes it empty.");
78+
</script>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="support/namespaces.js"></script>
5+
<script src="support/passthroughpolicy.js"></script>
6+
<script src="support/script-messages.js"></script>
7+
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#enforcement-in-scripts">
8+
<link rel="help" href="https://html.spec.whatwg.org/#prepare-the-script-element">
9+
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
10+
<!-- This test covers the following step from the "prepare the script element"
11+
algorithm, verifying that "source text" is the one after application of
12+
the default policy: "If el does not have a src content attribute: ...
13+
Switch on el's type:" -->
14+
<div id="container"></div>
15+
<script>
16+
const logMessageModulePath = "./support/logMessage-module.sub.js";
17+
18+
// Define a default policy that transforms the script's type to some valid
19+
// source content.
20+
trustedTypes.createPolicy("default", {
21+
createScript: (value, _, sink) => {
22+
window.log_message("CREATE_SCRIPT");
23+
window.log_message(sink);
24+
switch (value) {
25+
case "classic":
26+
return `window.log_message('CLASSIC');`;
27+
case "module":
28+
return `window.log_message('MODULE');`;
29+
case "importmap":
30+
return `{ "imports": { "${logMessageModulePath}?message=UNMAPPED": "${logMessageModulePath}?message=IMPORTMAP" }}`;
31+
}
32+
}
33+
});
34+
35+
promise_test(async t => {
36+
let messages = await script_messages_for(_ => {
37+
let script = create_html_script_with_untrusted_source_text("classic");
38+
script.setAttribute("type", "application/ecmascript");
39+
// Appending the script will log "CLASSIC".
40+
container.appendChild(script);
41+
});
42+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "CLASSIC"]);
43+
}, "Untrusted HTMLScriptElement with classic type uses the source text returned by the default policy.");
44+
45+
// Firefox disallows import map after a module load, so place this promise
46+
// test before the module test.
47+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1916277#c4
48+
promise_test(async t => {
49+
let messages = await script_messages_for(async _ => {
50+
let script = create_html_script_with_untrusted_source_text("importmap");
51+
script.setAttribute("type", "importmap");
52+
53+
// Appending the script sets up an import map for logMessageModulePath.
54+
container.appendChild(script);
55+
56+
// Importing logMessageModulePath will log message "IMPORTMAP"
57+
await import(`${logMessageModulePath}?message=UNMAPPED`);
58+
});
59+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "IMPORTMAP"]);
60+
}, "Untrusted HTMLScriptElement of importmap type uses the source text returned by the default policy.");
61+
62+
promise_test(async t => {
63+
let messages = await script_messages_for(async _ => {
64+
let script = create_html_script_with_untrusted_source_text("module");
65+
script.setAttribute("type", "module");
66+
67+
// Appending the script will log message "MODULE"
68+
container.appendChild(script);
69+
});
70+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "MODULE"]);
71+
}, "Untrusted HTMLScriptElement of module type uses the source text returned by the default policy.");
72+
</script>
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="support/namespaces.js"></script>
5+
<script src="support/passthroughpolicy.js"></script>
6+
<script src="support/script-messages.js"></script>
7+
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#enforcement-in-scripts">
8+
<link rel="help" href="https://html.spec.whatwg.org/#prepare-the-script-element">
9+
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
10+
<!-- This is the same test as script-enforcement-006 but for SVGScriptElement. -->
11+
<svg id="container"></svg>
12+
<script>
13+
const logMessageModulePath = "./support/logMessage-module.sub.js";
14+
15+
// Define a default policy that transforms the script's type to some valid
16+
// source content.
17+
trustedTypes.createPolicy("default", {
18+
createScript: (value, _, sink) => {
19+
window.log_message("CREATE_SCRIPT");
20+
window.log_message(sink);
21+
switch (value) {
22+
case "classic":
23+
return `window.log_message('CLASSIC');`;
24+
case "module":
25+
return `window.log_message('MODULE');`;
26+
case "importmap":
27+
return `{ "imports": { "${logMessageModulePath}?message=UNMAPPED": "${logMessageModulePath}?message=IMPORTMAP" }}`;
28+
}
29+
}
30+
});
31+
32+
promise_test(async t => {
33+
let messages = await script_messages_for(_ => {
34+
let script = create_svg_script_with_untrusted_source_text("classic");
35+
script.setAttribute("type", "application/ecmascript");
36+
// Appending the script will log "CLASSIC".
37+
container.appendChild(script);
38+
});
39+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "CLASSIC"]);
40+
}, "Untrusted SVGScriptElement with classic type uses the source text returned by the default policy.");
41+
42+
// Firefox disallows import map after a module load, so place this promise
43+
// test before the module test.
44+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1916277#c4
45+
promise_test(async t => {
46+
let messages = await script_messages_for(async _ => {
47+
let script = create_svg_script_with_untrusted_source_text("importmap");
48+
script.setAttribute("type", "importmap");
49+
50+
// Appending the script sets up an import map for logMessageModulePath.
51+
container.appendChild(script);
52+
53+
// Importing logMessageModulePath will log message "IMPORTMAP".
54+
await import(`${logMessageModulePath}?message=UNMAPPED`);
55+
});
56+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "IMPORTMAP"]);
57+
}, "Untrusted SVGScriptElement of importmap type uses the source text returned by the default policy.");
58+
59+
promise_test(async t => {
60+
let messages = await script_messages_for(async _ => {
61+
let script = create_svg_script_with_untrusted_source_text("module");
62+
script.setAttribute("type", "module");
63+
64+
// Appending the script will log message "MODULE".
65+
container.appendChild(script);
66+
});
67+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "MODULE"]);
68+
}, "Untrusted SVGScriptElement of module type uses the source text returned by the default policy.");
69+
</script>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="support/namespaces.js"></script>
5+
<script src="support/passthroughpolicy.js"></script>
6+
<script src="support/script-messages.js"></script>
7+
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#enforcement-in-scripts">
8+
<link rel="help" href="https://html.spec.whatwg.org/#prepare-the-script-element">
9+
<link rel="help" href="https://w3c.github.io/webappsec-csp/#should-block-inline">
10+
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
11+
<meta id="metaTagForScriptSrc" http-equiv="Content-Security-Policy" content="script-src 'nonce-script-messages' 'nonce-self' 'sha256-IpCtvKVFQbqDBhwCvQEsZoqgVXvAd6T2uRWd/Pz7FuI=' 'sha256-xanaWuoRdfLzI0+K8zpwr8eHi4RK2P6GglgCFXv0r00=' 'sha256-BPWjrQT1GMyyQ+6Fmycn7pSqh8L945ToMJ/nfGClLBc='">
12+
<!-- This test covers the following step from the "prepare the script element"
13+
algorithm, verifying that "source text" is the one after application of
14+
the default policy: "If el does not have a src content attribute, and the
15+
Should element's inline behavior be blocked by Content Security Policy?
16+
algorithm returns "Blocked" when given el, "script", and source text, then
17+
return." -->
18+
<div id="container"></div>
19+
<script nonce="self">
20+
const logMessageModulePath = "./support/logMessage-module.sub.js";
21+
22+
// Define a default policy that transforms the script's type to some valid
23+
// source content.
24+
function scriptTypeToValue(value) {
25+
switch (value) {
26+
case "classic":
27+
return `window.log_message('CLASSIC');`;
28+
case "module":
29+
return `window.log_message('MODULE');`;
30+
case "importmap":
31+
return `{ "imports": { "${logMessageModulePath}?message=UNMAPPED": "${logMessageModulePath}?message=IMPORTMAP" }}`;
32+
}
33+
}
34+
trustedTypes.createPolicy("default", {
35+
createScript: (value, _, sink) => {
36+
window.log_message("CREATE_SCRIPT");
37+
window.log_message(sink);
38+
return scriptTypeToValue(value);
39+
}
40+
});
41+
42+
promise_test(async t => {
43+
let classicHash = await base64_hash_for_inline_script(scriptTypeToValue("classic"), "SHA-256");
44+
let moduleHash = await base64_hash_for_inline_script(scriptTypeToValue("module"), "SHA-256");
45+
let importmapHash = await base64_hash_for_inline_script(scriptTypeToValue("importmap"), "SHA-256");
46+
let metaTagContent = document.getElementById("metaTagForScriptSrc").getAttribute("content");
47+
assert_equals(metaTagContent, `script-src 'nonce-script-messages' 'nonce-self' 'sha256-${classicHash}' 'sha256-${moduleHash}' 'sha256-${importmapHash}'`);
48+
}, "script-src CSP directive is properly set.");
49+
50+
promise_test(async t => {
51+
let messages = await script_messages_for(_ => {
52+
let script = create_html_script_with_untrusted_source_text("classic");
53+
script.setAttribute("type", "application/ecmascript");
54+
// Appending the script will log "CLASSIC".
55+
container.appendChild(script);
56+
});
57+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "CLASSIC"]);
58+
}, "Untrusted HTMLScriptElement with classic type uses the source text returned by the default policy for inline CSP check.");
59+
60+
// Firefox disallows import map after a module load, so place this promise
61+
// test before the module test.
62+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1916277#c4
63+
promise_test(async t => {
64+
let messages = await script_messages_for(async _ => {
65+
let script = create_html_script_with_untrusted_source_text("importmap");
66+
script.setAttribute("type", "importmap");
67+
68+
// Appending the script sets up an import map for logMessageModulePath.
69+
container.appendChild(script);
70+
71+
// Importing logMessageModulePath will log message "IMPORTMAP"
72+
await import(`${logMessageModulePath}?message=UNMAPPED`);
73+
});
74+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "IMPORTMAP"]);
75+
}, "Untrusted HTMLScriptElement of importmap type uses the source text returned by the default policy for inline CSP check.");
76+
77+
promise_test(async t => {
78+
let messages = await script_messages_for(async _ => {
79+
let script = create_html_script_with_untrusted_source_text("module");
80+
script.setAttribute("type", "module");
81+
82+
// Appending the script will log message "MODULE"
83+
container.appendChild(script);
84+
});
85+
assert_array_equals(messages, ["CREATE_SCRIPT", "HTMLScriptElement text", "MODULE"]);
86+
}, "Untrusted HTMLScriptElement of module type uses the source text returned by the default policy for inline CSP check.");
87+
</script>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="support/namespaces.js"></script>
5+
<script src="support/passthroughpolicy.js"></script>
6+
<script src="support/script-messages.js"></script>
7+
<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/#enforcement-in-scripts">
8+
<link rel="help" href="https://html.spec.whatwg.org/#prepare-the-script-element">
9+
<link rel="help" href="https://w3c.github.io/webappsec-csp/#should-block-inline">
10+
<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'">
11+
<meta id="metaTagForScriptSrc" http-equiv="Content-Security-Policy" content="script-src 'nonce-script-messages' 'nonce-self' 'sha256-IpCtvKVFQbqDBhwCvQEsZoqgVXvAd6T2uRWd/Pz7FuI=' 'sha256-xanaWuoRdfLzI0+K8zpwr8eHi4RK2P6GglgCFXv0r00=' 'sha256-BPWjrQT1GMyyQ+6Fmycn7pSqh8L945ToMJ/nfGClLBc='">
12+
<!-- This is the same test as script-enforcement-008 but for SVGScriptElement. -->
13+
<svg id="container"></svg>
14+
<script nonce="self">
15+
const logMessageModulePath = "./support/logMessage-module.sub.js";
16+
17+
// Define a default policy that transforms the script's type to some valid
18+
// source content.
19+
function scriptTypeToValue(value) {
20+
switch (value) {
21+
case "classic":
22+
return `window.log_message('CLASSIC');`;
23+
case "module":
24+
return `window.log_message('MODULE');`;
25+
case "importmap":
26+
return `{ "imports": { "${logMessageModulePath}?message=UNMAPPED": "${logMessageModulePath}?message=IMPORTMAP" }}`;
27+
}
28+
}
29+
trustedTypes.createPolicy("default", {
30+
createScript: (value, _, sink) => {
31+
window.log_message("CREATE_SCRIPT");
32+
window.log_message(sink);
33+
return scriptTypeToValue(value);
34+
}
35+
});
36+
37+
promise_test(async t => {
38+
let classicHash = await base64_hash_for_inline_script(scriptTypeToValue("classic"), "SHA-256");
39+
let moduleHash = await base64_hash_for_inline_script(scriptTypeToValue("module"), "SHA-256");
40+
let importmapHash = await base64_hash_for_inline_script(scriptTypeToValue("importmap"), "SHA-256");
41+
let metaTagContent = document.getElementById("metaTagForScriptSrc").getAttribute("content");
42+
assert_equals(metaTagContent, `script-src 'nonce-script-messages' 'nonce-self' 'sha256-${classicHash}' 'sha256-${moduleHash}' 'sha256-${importmapHash}'`);
43+
}, "script-src CSP directive is properly set.");
44+
45+
promise_test(async t => {
46+
let messages = await script_messages_for(_ => {
47+
let script = create_svg_script_with_untrusted_source_text("classic");
48+
script.setAttribute("type", "application/ecmascript");
49+
// Appending the script will log "CLASSIC".
50+
container.appendChild(script);
51+
});
52+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "CLASSIC"]);
53+
}, "Untrusted SVGScriptElement with classic type uses the source text returned by the default policy for inline CSP check.");
54+
55+
// Firefox disallows import map after a module load, so place this promise
56+
// test before the module test.
57+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1916277#c4
58+
promise_test(async t => {
59+
let messages = await script_messages_for(async _ => {
60+
let script = create_svg_script_with_untrusted_source_text("importmap");
61+
script.setAttribute("type", "importmap");
62+
63+
// Appending the script sets up an import map for logMessageModulePath.
64+
container.appendChild(script);
65+
66+
// Importing logMessageModulePath will log message "IMPORTMAP"
67+
await import(`${logMessageModulePath}?message=UNMAPPED`);
68+
});
69+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "IMPORTMAP"]);
70+
}, "Untrusted SVGScriptElement of importmap type uses the source text returned by the default policy for inline CSP check.");
71+
72+
promise_test(async t => {
73+
let messages = await script_messages_for(async _ => {
74+
let script = create_svg_script_with_untrusted_source_text("module");
75+
script.setAttribute("type", "module");
76+
77+
// Appending the script will log message "MODULE"
78+
container.appendChild(script);
79+
});
80+
assert_array_equals(messages, ["CREATE_SCRIPT", "SVGScriptElement text", "MODULE"]);
81+
}, "Untrusted SVGScriptElement of module type uses the source text returned by the default policy for inline CSP check.");
82+
</script>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
window.log_message("{{GET[message]}}");

0 commit comments

Comments
 (0)