diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/trusted-types | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/trusted-types')
119 files changed, 6219 insertions, 0 deletions
diff --git a/testing/web-platform/tests/trusted-types/DOMParser-parseFromString-regression.html b/testing/web-platform/tests/trusted-types/DOMParser-parseFromString-regression.html new file mode 100644 index 0000000000..941d1750b4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/DOMParser-parseFromString-regression.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<meta http-equiv="Content-Security-Policy" content="blabla"> +<body> +<div id="target"></div> +<div id="probe"></div> +<script> +test(t => { + // Regression test for crbug.com/1030830. (Should work in any browser, though.) + // + // The top-level doc has a CSP that doesn't do anything interesting. We'll + // parse a doc and create an iframe with an embedded CSP, and will ensure that + // the CSP applies to the frame, but not the top-level doc. + const target = document.getElementById("target"); + const probe = document.getElementById("probe"); + probe.innerHTML = "probe"; + + const doc = new DOMParser().parseFromString(` + <body><div id="probe"></div></body>"`, "text/html"); + probe.innerHTML = "probe"; + + const frame = document.createElement("iframe"); + frame.src = `data:text/html;${encodeURI(doc.documentElement.outerHTML)}`; + frame.id = "frame"; + target.appendChild(frame); + const frame_probe = document.getElementById("frame").contentDocument.getElementById("probe"); + probe.innerHTML = "probe"; + assert_throws_js(TypeError, _ => { frame_probe.innnerHTML = "probe" }); +}, "Regression test for TT changes to parseFromString."); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/DOMParser-parseFromString.html b/testing/web-platform/tests/trusted-types/DOMParser-parseFromString.html new file mode 100644 index 0000000000..2dfc37686b --- /dev/null +++ b/testing/web-platform/tests/trusted-types/DOMParser-parseFromString.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let parser = new DOMParser(); + let doc = parser.parseFromString(html, "text/html"); + assert_equals(doc.body.innerText, RESULTS.HTML); + }, "document.innerText assigned via policy (successful HTML transformation)."); + + test(t => { + var parser = new DOMParser(); + var doc = parser.parseFromString(null, "text/html"); + assert_equals(doc.body.innerText, "null"); + }, "document.innerText = null."); +</script> diff --git a/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html b/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html new file mode 100644 index 0000000000..2ad47555a9 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'"> +<body> +<script> + async_test(t => { + window.timeoutTrustedTest = t; + let policy = createScript_policy(window, 'timeout'); + let script = policy.createScript("window.timeoutTrustedTest.done();"); + setTimeout(script); + }, "window.setTimeout assigned via policy (successful Script transformation)."); + + async_test(t => { + window.intervalTrustedTest = t; + let policy = createScript_policy(window, 'script'); + let script = policy.createScript("window.intervalTrustedTest.done();"); + setInterval(script); + }, "window.setInterval assigned via policy (successful Script transformation)."); + + trustedTypes.createPolicy("default", {createScript: s => "window." + s + ".done()"}); + + async_test(t => { + window.timeoutStringTest = t; + let script = "timeoutStringTest"; + setTimeout(script); + }, "window.setTimeout assigned via default policy (successful Script transformation)."); + + async_test(t => { + window.intervalStringTest = t; + let script = "intervalStringTest"; + setInterval(script); + }, "window.setInterval assigned via default policy (successful Script transformation)."); + + async_test(t => { + window.timeoutFunctionTest = t; + let script = () => window.timeoutFunctionTest.done(); + setTimeout(script); + }, "window.setTimeout assigned with a function handler shouldn't go through default policy."); + + async_test(t => { + window.intervalFunctionTest = t; + let script = () => window.intervalFunctionTest.done(); + setInterval(script); + }, "window.setInterval assigned with a function handler shouldn't go through default policy."); +</script> diff --git a/testing/web-platform/tests/trusted-types/Document-execCommand.html b/testing/web-platform/tests/trusted-types/Document-execCommand.html new file mode 100644 index 0000000000..7619133655 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Document-execCommand.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<link rel="author" title="Daniel Vogelheim" href="mailto:vogelheim@chromium.org"></link> +<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/"></link> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + // Test that execCommand continues to work if Trusted Types is not enabled. + const commands = [ "insertHTML", "paste" ]; + for (const command of commands) { + test(t => { + document.execCommand(command, false, "<em>Hello World</em>"); + }, `Document.execCommand("${command}") works as usual.`); + } +</script> diff --git a/testing/web-platform/tests/trusted-types/Document-write.html b/testing/web-platform/tests/trusted-types/Document-write.html new file mode 100644 index 0000000000..87e9e72469 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Document-write.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + // TrustedHTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + document.write(html); + assert_true(document.body.innerText.indexOf(RESULTS.HTML) !== -1); + }, "document.write with html assigned via policy (successful transformation)."); +</script> diff --git a/testing/web-platform/tests/trusted-types/Element-insertAdjacentHTML.html b/testing/web-platform/tests/trusted-types/Element-insertAdjacentHTML.html new file mode 100644 index 0000000000..6a9329b3fb --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Element-insertAdjacentHTML.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container'); + + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let before = 'before'; + let after = 'after'; + let htmlBefore = p.createHTML(before); + let htmlAfter = p.createHTML(after); + + var d = document.createElement('div'); + container.appendChild(d); + + d.insertAdjacentHTML('beforebegin', html); + assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.previousSibling.data, RESULTS.HTML); + + d.insertAdjacentHTML('afterbegin', htmlBefore); + d.insertAdjacentHTML('beforeend', htmlAfter); + assert_equals(d.innerHTML, before + after); + + d.insertAdjacentHTML('afterend', html); + assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.nextSibling.data, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "insertAdjacentHTML with html assigned via policy (successful HTML transformation)."); +</script> diff --git a/testing/web-platform/tests/trusted-types/Element-insertAdjacentText.html b/testing/web-platform/tests/trusted-types/Element-insertAdjacentText.html new file mode 100644 index 0000000000..f6221362c1 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Element-insertAdjacentText.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container'); + + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let before = 'before'; + let after = 'after'; + let htmlBefore = p.createHTML(before); + let htmlAfter = p.createHTML(after); + + var d = document.createElement('div'); + container.appendChild(d); + + d.insertAdjacentHTML('beforebegin', html); + assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.previousSibling.data, RESULTS.HTML); + + d.insertAdjacentHTML('afterbegin', htmlBefore); + d.insertAdjacentHTML('beforeend', htmlAfter); + assert_equals(d.innerHTML, before + after); + + d.insertAdjacentHTML('afterend', html); + assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.nextSibling.data, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "insertAdjacentHTML with html assigned via policy (successful HTML transformation)."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/Element-outerHTML.html b/testing/web-platform/tests/trusted-types/Element-outerHTML.html new file mode 100644 index 0000000000..c8daddfe99 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Element-outerHTML.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container'); + + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + + var d = document.createElement('div'); + document.querySelector('#container').appendChild(d); + d.outerHTML = html; + assert_equals(container.innerText, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "outerHTML with html assigned via policy (successful HTML transformation)."); +</script> diff --git a/testing/web-platform/tests/trusted-types/Element-setAttribute.html b/testing/web-platform/tests/trusted-types/Element-setAttribute.html new file mode 100644 index 0000000000..cd6617915b --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Element-setAttribute.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> +</head> +<body> +<script> + // TrustedScriptURL Assignments + let scriptTestCases = [ + [ 'embed', 'src' ], + [ 'script', 'src' ] + ]; + + scriptTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_script_url_explicit_set(window, c, t, c[0], c[1], RESULTS.SCRIPTURL); + }, c[0] + "." + c[1] + " assigned via policy (successful ScriptURL transformation)"); + }); + + // TrustedHTML Assignments + let HTMLTestCases = [ + [ 'iframe', 'srcdoc' ] + ]; + + HTMLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_html_explicit_set(window, c, t, c[0], c[1], RESULTS.HTML); + }, c[0] + "." + c[1] + " assigned via policy (successful HTML transformation)"); + }); + + // Other attributes can be assigned with TrustedTypes or strings or null values + test(t => { + assert_element_accepts_trusted_script_url_explicit_set(window, 'scriptsrc1', t, 'script', 'src', RESULTS.SCRIPTURL); + }, "script.src assigned via policy (successful script transformation)"); + + test(t => { + assert_element_accepts_non_trusted_type_explicit_set('a', 'rel', 'A string', 'A string'); + }, "a.rel accepts strings"); + + test(t => { + assert_element_accepts_non_trusted_type_explicit_set('a', 'rel', null, 'null'); + }, "a.rel accepts null"); +</script> diff --git a/testing/web-platform/tests/trusted-types/Element-setAttributeNS.html b/testing/web-platform/tests/trusted-types/Element-setAttributeNS.html new file mode 100644 index 0000000000..67e8236feb --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Element-setAttributeNS.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> +</head> +<body> +<script> + test(t => { + assert_element_accepts_trusted_html_set_ns(window, '0', t, 'a', 'b', RESULTS.HTML); + }, "Element.setAttributeNS assigned via policy (successful HTML transformation)"); + + test(t => { + assert_element_accepts_trusted_script_set_ns(window, '1', t, 'a', 'b', RESULTS.SCRIPT); + }, "Element.setAttributeNS assigned via policy (successful Script transformation)"); + + test(t => { + assert_element_accepts_trusted_script_url_set_ns(window, '2', t, 'a', 'b', RESULTS.SCRIPTURL); + }, "Element.setAttributeNS assigned via policy (successful ScriptURL transformation)"); + +// TODO: Is there any non-URL, namespaced accessor left? +/* + test(t => { + let p = createURL_policy(window, '5'); + let url = p.createURL(INPUTS.URL); + + let elem = document.createElementNS("http://www.w3.org/2000/svg", "image"); + elem.setAttributeNS("http://www.w3.org/1999/xlink", "href", url); + let attr_node = elem.getAttributeNodeNS("http://www.w3.org/1999/xlink", "href"); + assert_equals(attr_node.value + "", RESULTS.URL); + }, "Element.setAttributeNS accepts a URL on <svg:image xlink:href/>"); +*/ + +</script> diff --git a/testing/web-platform/tests/trusted-types/GlobalEventHandlers-onclick.html b/testing/web-platform/tests/trusted-types/GlobalEventHandlers-onclick.html new file mode 100644 index 0000000000..0fdde778cc --- /dev/null +++ b/testing/web-platform/tests/trusted-types/GlobalEventHandlers-onclick.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'"> +<body> +<div id="container"></div> +<script> +var container = document.querySelector('#container'); +const policy = createScript_policy(window, 'onclick'); +const policy_html = createHTML_policy(window, 'onclick-html'); + +// Trusted Type assignments do not throw. +async_test(t => { + window.onclickDone1 = t.step_func_done(); + let script = policy.createScript("window.onclickDone1();"); + let el = document.createElement('a'); + el.setAttribute('onclick', script); + container.appendChild(el); + el.click(); +}, "a.setAttribte('onclick') sets a trusted script."); + +// Unsuitable TrustedType assignments do throw. +async_test(t => { + window.onclickFail1 = t.unreached_func(); + let script = policy_html.createHTML("window.onclickFail1();"); + let el = document.createElement('a'); + try { + el.setAttribute('onclick', script); + container.appendChild(el); + el.click(); + } catch (e) { + t.done(); + } + assert_unreached(); +}, "a.setAttribute('onclick') sets an unsuitable trusted type."); + +// So do plain test assignments. +async_test(t => { + window.onclickFail2 = t.unreached_func(); + let el = document.createElement('a'); + try { + el.setAttribute("onclick", "window.onclickFail2();"); + container.appendChild(el); + el.click(); + } catch (e) { + t.done(); + } + assert_unreached(); +}, "a.setAttribute('click') sets a test string."); +/* +// Trusted Type assignments via property access does not throw. +async_test(t => { + window.onclickDone2 = t.step_func_done(); + let script = policy.createScript("window.onclickDone2();"); + let el = document.createElement('a'); + el.onclick = script; + container.appendChild(el); + el.click(); +}, "a.onclick assigned via policy (successful Script transformation)."); + +// Unsuitable TrustedType assignments do throw. +async_test(t => { + window.onclickFail3 = t.unreached_func(); + let script = policy_html.createHTML("window.onclickFail3();"); + let el = document.createElement('a'); + try { + el.onclick = script; + container.appendChild(el); + el.click(); + } catch (e) { + t.done(); + } + assert_unreached(); +}, "a.onclick assigned via an unsuitable policy."); + +// So do plain test assignments. +async_test(t => { + window.onclickFail4 = t.unreached_func(); + let el = document.createElement('a'); + try { + el.onclick = window.onclickFail4(); + container.appendChild(el); + el.click(); + } catch (e) { + t.done(); + } + assert_unreached(); +}, "a.onclick assigned a test string."); +*/ +</script> diff --git a/testing/web-platform/tests/trusted-types/HTMLElement-generic.html b/testing/web-platform/tests/trusted-types/HTMLElement-generic.html new file mode 100644 index 0000000000..8e54fa9c57 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/HTMLElement-generic.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> +</head> +<body> +<script> +const policy = trustedTypes.createPolicy("testpolicy", { + createScript: s => s, + createHTML: s => s, + createScriptURL: s => s, +}); + +function getTrusted(element, attr) { + const type = trustedTypes.getPropertyType(element, attr); + if (type == "TrustedScript") { + return policy.createScript("2+2"); + } else if (type == "TrustedScriptURL") { + return policy.createScript("https://example.test/"); + } else if (type == "TrustedHTML") { + return policy.createHTML("<b>hello</b>"); + } else { + return "a simple string"; + } +} + +// This test will run a simple, TT-relevant assignment, in a number of +// circumstances. We've had issues where subtle difference in DOM behaviour - +// for example a connected element or a non-connected element - produce +// different results, and no test catching it because the tests were written +// to do it one particular way. So this test does one thing, but in all the +// different ways we can think of. +// +// - With TT disabled or enabled, +// - with any of the trusted types, +// - with a string or a TT value, +// - with a element that's connected to the DOM (or not). +// +// Run the set of tests, assuming that is_tt_enabled reflects whether Trusted +// Types is currently enabled (& enforced) or not. +function runTests(is_tt_enabled) { + for (const [element, attr] of [ + [ 'embed', 'src' ], + [ 'script', 'src' ], + [ 'div', 'innerHTML' ], + [ 'iframe', 'srcdoc' ], + [ 'script', 'text' ], + [ 'script', 'innerText' ], + [ 'script', 'textContent' ], + ]) { + const trusted = getTrusted(element, attr); + for (const value of [trusted, trusted.toString()]) { + for (const connected of [true, false]) { + + const expect_exception = is_tt_enabled && + value.constructor.name != trustedTypes.getPropertyType(element, attr); + test(t => { + const elem = document.createElement(element); + if (connected) document.body.appendChild(elem); + if (expect_exception) { + assert_throws_js(TypeError, _ => { elem[attr] = value; }); + } else { + elem[attr] = value; + } + }, `${is_tt_enabled ? "TT enabled" : "TT disabled"}: ${element}.${attr} = ${value.constructor.name} on a ${connected ? "connected" : "non-connected"} element.`); + } + } + } +} + +// Run the tests without TT first. +runTests(false); + +// Now run the tests a second time, with TT enabled. To accomplish this, insert +// a suitable <meta> element. +const meta = document.createElement("meta"); +meta.setAttribute("http-equiv", "Content-Security-Policy"); +meta.setAttribute("content", "require-trusted-types-for 'script';"); +document.head.appendChild(meta); +runTests(true); +</script> diff --git a/testing/web-platform/tests/trusted-types/HTMLScriptElement-in-xhtml-document.tentative.https.xhtml b/testing/web-platform/tests/trusted-types/HTMLScriptElement-in-xhtml-document.tentative.https.xhtml new file mode 100644 index 0000000000..6cf49f922a --- /dev/null +++ b/testing/web-platform/tests/trusted-types/HTMLScriptElement-in-xhtml-document.tentative.https.xhtml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <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> + // This this a regresion test that verifies that in-tree script element in + // an xhtml document still works correctly. The test itself doesn't do much. + test(t => { + t.done(); + }, "Test whether a script element still executes for XHTML documents."); +</script> +</body> +</html> + diff --git a/testing/web-platform/tests/trusted-types/HTMLScriptElement-internal-slot.html b/testing/web-platform/tests/trusted-types/HTMLScriptElement-internal-slot.html new file mode 100644 index 0000000000..3a4b905f46 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/HTMLScriptElement-internal-slot.html @@ -0,0 +1,159 @@ +<!DOCTYPE html> +<html> +<head> + <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> + promise_test(t => { + // Setup: We create an <iframe> and trigger loading the document. We use + // WPTserve's ?pipe functionality to make sure it gets delivered in chunks + // and that parsing will be deferred. + var frame = document.createElement("iframe"); + document.body.appendChild(frame); + frame.src = "support/HTMLScriptElement-internal-slot-support.html?pipe=trickle(200:d1)"; + + // This function tries to find the first <script> element in our iframe and + // manipulated it with DOM APIs (appendChild + createTextNode). If it + // doesn't find the script element then it'll just enqueue itself again + // in 100ms. + var manipulator = _ => { + let script = frame.contentDocument.getElementsByTagName("script")[0]; + if (script) { + script.appendChild(frame.contentDocument.createTextNode("/*byapi*/")); + } else { + t.step_timeout(manipulator, 100); + } + } + manipulator(); + + // Now we'll wait for the postMessages to arrive. We expect the iframe's + // first message to be blocked by Trusted Types, since the manipulator + // above should have manipulated it (while loading). The second one should + // pass. + return new Promise((resolve, reject) => { + window.addEventListener("message", e => { + if (e.data.includes("first")) { + reject("First message should have been blocked: " + e.data); + } else if (e.data.includes("second")) { + resolve(); + } else { + reject("Unknown message: " + e.data); + } + }); + }); + }, "Test TT application when manipulating <script> elements during loading."); + + // Test that a script set via textContent or innerTest actually executes + // (and not only doesn't throw an exception, which it wouldn't do due to the + // "internal slot" mechanism). + const policy = trustedTypes.createPolicy("testpolicy", { + createScript: x => x, createScriptURL: x => x}); + promise_test(t => { + const s = document.createElement("script"); + document.body.appendChild(s); + s.textContent = policy.createScript("window.postMessage('hello');"); + return new Promise(resolve => { + window.addEventListener("message", e => { + if (e.data == "hello") resolve(); + }); + }); + }, "Script set via .textContent executes on a connected HTMLScriptElement."); + promise_test(t => { + const s = document.createElement("script"); + s.textContent = policy.createScript("window.postMessage('world');"); + document.body.appendChild(s); + return new Promise(resolve => { + window.addEventListener("message", e => { + if (e.data == "world") resolve(); + }); + }); + }, "Script set via .textContent executes on an unconnected HTMLScriptElement."); + promise_test(t => { + const s = document.createElement("script"); + document.body.appendChild(s); + s.innerText = policy.createScript("window.postMessage('hello');"); + return new Promise(resolve => { + window.addEventListener("message", e => { + if (e.data == "hello") resolve(); + }); + }); + }, "Script set via .innerText executes on a connected HTMLScriptElement."); + promise_test(t => { + const s = document.createElement("script"); + s.innerText= policy.createScript("window.postMessage('world');"); + document.body.appendChild(s); + return new Promise(resolve => { + window.addEventListener("message", e => { + if (e.data == "world") resolve(); + }); + }); + }, "Script set via .innerText executes on an unconnected HTMLScriptElement."); + + // Test that interactions between the script content, the .src attribute, and + // exceptions still work correctly with TT and the "internal slot" thingy. + promise_test(t => { + const s = document.createElement("script"); + s.textContent = policy.createScript("window.postMessage('script body');"); + try { + s.src = "support/HTMLScriptElement-internal-slot-support.js"; + } catch (e) {} + document.body.appendChild(s); + return new Promise((resolve, reject) => { + window.addEventListener("message", e => { + // .src was set with plain string => should not have been accepted. + if (e.data == "script body") resolve(); + if (e.data == "script url") reject(); + }); + }); + }, "Setting .src to a plain string should throw an exception and not modify the script state, on an unconnected script element."); + + promise_test(t => { + const s = document.createElement("script"); + s.textContent = policy.createScript("window.postMessage('script body');"); + s.src = policy.createScriptURL("support/HTMLScriptElement-internal-slot-support.js"); + document.body.appendChild(s); + return new Promise((resolve, reject) => { + window.addEventListener("message", e => { + // .src was set with a TrustedScriptURL => the .src should get executed. + if (e.data == "script body") reject(); + if (e.data == "script url") resolve(); + }); + }); + }, "Setting .src to a TrustedScriptURL should work and should execute the referenced script instead of the script body, on an unconnected script element."); + + promise_test(t => { + const s = document.createElement("script"); + document.body.appendChild(s); + s.textContent = policy.createScript("window.postMessage('script body');"); + try { + s.src = "support/HTMLScriptElement-internal-slot-support.js"; + } catch (e) {} + return new Promise((resolve, reject) => { + window.addEventListener("message", e => { + // .src was set with plain string => should not have been accepted. + if (e.data == "script body") resolve(); + if (e.data == "script url") reject(); + }); + }); + }, "Setting .src to a plain string should throw an exception and not modify the script state, on a connected script element."); + + promise_test(t => { + const s = document.createElement("script"); + s.textContent = policy.createScript("window.postMessage('script body');"); + s.src = policy.createScriptURL("support/HTMLScriptElement-internal-slot-support.js"); + document.body.appendChild(s); + return new Promise((resolve, reject) => { + window.addEventListener("message", e => { + // .src was set with a TrustedScriptURL => the .src should get executed. + if (e.data == "script body") reject(); + if (e.data == "script url") resolve(); + }); + }); + }, "Setting .src to a TrustedScriptURL should work and should execute the referenced script instead of the script body, on a onnected script element."); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/META.yml b/testing/web-platform/tests/trusted-types/META.yml new file mode 100644 index 0000000000..04061ac0f6 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/META.yml @@ -0,0 +1,3 @@ +spec: https://w3c.github.io/trusted-types/dist/spec/ +suggested_reviewers: + - mikewest diff --git a/testing/web-platform/tests/trusted-types/Node-multiple-arguments.html b/testing/web-platform/tests/trusted-types/Node-multiple-arguments.html new file mode 100644 index 0000000000..64b04656d3 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Node-multiple-arguments.html @@ -0,0 +1,66 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> +</head> +<body> +<div id="container"></div> +<script> + 'use strict'; + const container = document.querySelector("#container"); + const policy = window.trustedTypes.createPolicy("policy", { + createScript: t => t, + }); + function stringify(arg) { + return "textContent" in Object.getPrototypeOf(arg) ? arg.textContent : arg.toString() + } + + // This test case mirrors the block-Node-multiple-arguments case except + // that, because Trusted Types is not enabled, no exceptions should be + // thrown anywhere. + const targets = ["div", "script"]; + const all_args = [ + [ policy.createScript("'createScript';") ], + [ policy.createScript("'createScript #1';"), policy.createScript("'#2;'") ], + [ "'plain text';" ], + [ "'plain text #1';", "'plain text #2';" ], + [ document.createTextNode("'node';") ], + [ document.createTextNode("'node #1';"), + document.createTextNode("'node #2';") ], + [ "'mixed';", document.createTextNode("'node';") ], + [ "'mixed';", policy.createScript("'script';") ], + [ document.createTextNode("'node';"), + policy.createScript("'script';") ], + ]; + + for (const target of targets) { + for (const args of all_args) { + + for (const setter of [container.replaceWith, container.after, container.before]) { + test(t => { + var outer = document.createElement(target); + container.appendChild(outer); + var inner = document.createElement("p"); + outer.appendChild(inner); + setter.apply(inner, args); + assert_equals(outer.textContent, args.map(stringify).join("")); + + }, `${setter.name}(${args.toString()}) on <${target}> should pass`); + } + + for (const setter of [container.append, container.prepend]) { + test(t => { + let outer = document.createElement(target); + container.appendChild(outer); + setter.apply(outer, args); + assert_equals(outer.textContent, args.map(stringify).join("")); + }, `${setter.name}(${args.toString()}) on <${target}> should pass`); + } + + } + } +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/Range-createContextualFragment.html b/testing/web-platform/tests/trusted-types/Range-createContextualFragment.html new file mode 100644 index 0000000000..3a880a5377 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Range-createContextualFragment.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + var result = range.createContextualFragment(html); + assert_equals(result.textContent, RESULTS.HTML); + }, "range.createContextualFragment assigned via policy (successful HTML transformation)."); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedType-AttributeNodes.html b/testing/web-platform/tests/trusted-types/TrustedType-AttributeNodes.html new file mode 100644 index 0000000000..6b00665700 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedType-AttributeNodes.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<head> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + // This is a set of tests that try to make sure various ways of setting + // attributes (directly; via an attribute node; etc.) are all being treated + // identically. For background, se:: + // https://github.com/w3c/webappsec-trusted-types/issues/47 + + const elems = ["div", "script", "embed", "iframe"]; + const attrs = ["src", "srcdoc", "onclick", "bla"]; + + const policy_callback = s => ("" + s + " [policy]"); + const policy = trustedTypes.createPolicy("policy", { + "createHTML": policy_callback, + "createScript": policy_callback, + "createScriptURL": policy_callback, + }); + + // Returns either the string-ified result value of fn, or an exception type. + function try_get(fn) { + try { + return "" + fn(); + } catch(e) { + return "" + e.name; + } + } + + // Returns a 'trusted' version of value, if element.attribute requires it. + function trusted(element, attribute, value) { + switch (trustedTypes.getAttributeType(element, attribute)) { + case 'TrustedHTML': value = policy.createHTML(value); break; + case 'TrustedScript': value = policy.createScript(value); break; + case 'TrustedScriptURL': value = policy.createScriptURL(value); break; + } + return value; + } + + for (elem of elems) { + for (attr of attrs) { + const reference = try_get(_ => { + const e = document.createElement(elem); + e.setAttribute(attr, "hello"); + return e.getAttribute(attr); + }); + + // Event handlers (like "onclick") apply to all elements, so we don't + // really have 'harmless' element we can attach them to. Hence this test + // case doesn't apply. + if (attr != "onclick") { + test(t => { + const harmless = document.createElement("div"); + harmless.setAttribute(attr, "hello"); + const node = harmless.getAttributeNode(attr); + harmless.removeAttributeNode(node); + + const actual = try_get(_ => { + const e = document.createElement(elem); + e.setAttributeNode(node); + return e.getAttribute(attr); + }); + assert_equals(actual, reference); + }, `Set attribute via attribute node on ${elem}.${attr}.`); + } + + test(t => { + const e = document.createElement(elem); + e.setAttribute(attr, trusted(elem, attr, "123")); + const actual = try_get(_ => { + e.getAttributeNode(attr).value = "hello"; + return e.getAttribute(attr); + }); + assert_equals(actual, reference); + }, `Set attribute via attributenode.value on ${elem}.${attr}.`); + + test(t => { + const e = document.createElement(elem); + const attrnode = document.createAttribute(attr); + attrnode.value = "hello"; + const actual = try_get(_ => { + e.attributes.setNamedItem(attrnode); + return e.getAttribute(attr); + }); + assert_equals(actual, reference); + }, `Set attribute via NamedNodeMap.setNamedItem on ${elem}.${attr}.`); + } + } +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicy-CSP-no-name.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicy-CSP-no-name.html new file mode 100644 index 0000000000..b1c7f51ddb --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicy-CSP-no-name.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="trusted-types"> +<body> +<script> + // No name given test + test(t => { + assert_throws_js(TypeError, + () => window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ), + "createPolicy with an empty trusted-types CSP directive"); + }, "No name list given - policy creation fails."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicy-CSP-wildcard.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicy-CSP-wildcard.html new file mode 100644 index 0000000000..cdc683dad3 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicy-CSP-wildcard.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<body> +<script> + test(t => { + let policy = window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + assert_equals(policy.name, 'SomeName'); + }, "CSP supports wildcards."); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicy-createXXX.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicy-createXXX.html new file mode 100644 index 0000000000..112d1ae636 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicy-createXXX.html @@ -0,0 +1,110 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<body> +<script> + test(t => { + const p1 = trustedTypes.createPolicy("policyHTMLAndScript", { + createHTML: s => s, + createScript: s => s + }); + assert_throws_js(TypeError, _ => { p1.createScriptURL("foo"); }); + + const p2 = trustedTypes.createPolicy("policyURLAndScriptURL", { + createScriptURL: s => s + }); + assert_throws_js(TypeError, _ => { p2.createHTML("foo"); }); + assert_throws_js(TypeError, _ => { p2.createScript("foo"); }); + }, "calling undefined callbacks throws"); + + test(t => { + const noopPolicy = { + createHTML: (s) => s, + createScriptURL: (s) => s, + createScript: (s) => s, + }; + policy = trustedTypes.createPolicy(Math.random(), noopPolicy, true); + let el = document.createElement("div"); + + el.title = policy.createHTML(INPUTS.SCRIPTURL); + assert_equals(el.title, INPUTS.SCRIPTURL); + }, "Attributes without type constraints will work as before."); + + test(t => { + const policy = trustedTypes.createPolicy("nullpolicy", null); + assert_throws_js(TypeError, _ => { policy.createScriptURL("foo"); }); + assert_throws_js(TypeError, _ => { policy.createHTML("foo"); }); + assert_throws_js(TypeError, _ => { policy.createScript("foo"); }); + }, "trustedTypes.createPolicy(.., null) creates empty policy."); + + + // testCases contains a list of policy functions and expected results (when + // called with a given default argument). They also use various helper + // variables (declared just below) to test side effects or interactions of + // global vars/functions with policy callbacks. + let aGlobalVarForSideEffectTesting = "global"; + var aGlobalObject = { "foo": "well," }; + function aGlobalFunction(s) { return this.foo + " " + s; } + function anotherGlobalFunction(s) { return s + "#" + this.foo; } + var foo = "a global var named foo"; + + const stringTestCases = [ + [ s => s, "whatever" ], + [ s => null, "" ], + [ s => "well, " + s, "well, whatever" ], + [ s => { throw new Error() }, Error ], + [ s => { aGlobalVarForSideEffectTesting = s; return s }, "whatever" ], + [ s => aGlobalVarForSideEffectTesting + s, "whateverwhatever" ], + [ aGlobalFunction.bind(aGlobalObject), "well, whatever" ], + [ s => aGlobalFunction(s), "a global var named foo whatever" ], + ]; + + const urlTestCases = [ + [ s => s, INPUTS.SCRIPTURL ], + [ s => null, "" ], + [ s => s + "#duck", INPUTS.SCRIPTURL + "#duck" ], + [ s => { throw new Error() }, Error ], + [ s => s + "#" + aGlobalVarForSideEffectTesting, + INPUTS.SCRIPTURL + "#global" ], + [ anotherGlobalFunction.bind(aGlobalObject), INPUTS.SCRIPTURL + "#well," ], + [ s => anotherGlobalFunction(s), + INPUTS.SCRIPTURL + "#a global var named foo" ], + ]; + + function policyBuilder(trustedMethodName, trustedType, defaultArg) { + return function(name, fn) { + let options = {}; + options[trustedMethodName] = fn; + let policy = window.trustedTypes.createPolicy(name, options); + let result = policy[trustedMethodName](defaultArg); + assert_true(result instanceof trustedType); + return result; + } + } + + const testCases = [ + [TrustedHTML, "createHTML", "whatever", stringTestCases], + [TrustedScript, "createScript", "whatever", stringTestCases], + [TrustedScriptURL, "createScriptURL", INPUTS.SCRIPTURL, urlTestCases], + ]; + + // Iterate over all trusted types, iterate over all test cases. + // For each, build the suitable policy and check the result. + for (let [trusted_class, trusted_method, default_arg, test_cases] of testCases) { + aGlobalVarForSideEffectTesting = "global"; + let builder = policyBuilder(trusted_method, trusted_class, default_arg); + for (let [index, [policy_fn, value]] of test_cases.entries()) { + let subtest_name = "TestPolicy" + trusted_class.name + index; + test(t => { + if (typeof value == "object" || typeof value == "function") { + assert_throws_js(value, () => builder(subtest_name, policy_fn)); + } else { + assert_equals("" + builder(subtest_name, policy_fn), value); + } + }, subtest_name + " (" + trusted_class.name + ": " + + policy_fn.toString() + ")"); + } + } +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-blocking.tentative.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-blocking.tentative.html new file mode 100644 index 0000000000..6ae71b6988 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-blocking.tentative.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" content="trusted-types *"> + +<body> +<script> + + test(t => { + event_listener = (e) => { + assert_equals(e.policyName, "default"); + e.preventDefault(); // don't let this policy be created. + }; + trustedTypes.addEventListener('beforecreatepolicy', event_listener, {once:true}); + assert_throws_dom("NotAllowedError", () => trustedTypes.createPolicy("default", {})); + }, 'Block Trusted Type policy creation by event listener.'); + + test(t => { + let flag = false; + event_listener = (e) => { + assert_equals(e.policyName, "myPolicy"); + flag = true; + }; + trustedTypes.addEventListener('beforecreatepolicy', event_listener, {once:true}); + trustedTypes.createPolicy("myPolicy", {}); + assert_equals(flag, true); + }, 'Trusted Type policy creation.'); + + test(t => { + event_listener = (e) => { + if (e.policyName === "default") + e.preventDefault(); + }; + trustedTypes.addEventListener('beforecreatepolicy', event_listener, {once:true}); + assert_throws_dom("NotAllowedError", () => trustedTypes.createPolicy("default", {})); + trustedTypes.createPolicy("newPolicy", {}); + }, 'Block only default Trusted Type policy creation.'); + + test(t => { + event_listener = (e) => { + assert_unreached(); + }; + trustedTypes.createPolicy("test", {}); + trustedTypes.addEventListener('beforecreatepolicy', event_listener, {once:true}); + }, 'Policy creation called before addEventListener function will not reached the listener.'); + +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-constants.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-constants.html new file mode 100644 index 0000000000..551084ca4b --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-constants.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<body> +<script> + test(t => { + const empty = trustedTypes.emptyHTML; + assert_true(trustedTypes.isHTML(empty)); + assert_equals(empty.toString(), ""); + }, 'trustedTypes.emptyHTML returns the intended value.'); + + test(t => { + try { trustedTypes.emptyHTML = 'fake'; } catch { } + assert_true(trustedTypes.isHTML(trustedTypes.emptyHTML)); + assert_equals(trustedTypes.emptyHTML.toString(), ""); + }, 'trustedTypes.emptyHTML cannot be redefined.'); + + test(t => { + try { Object.defineProperty(TrustedTypes, 'emptyHTML', 'fake'); } catch { } + assert_true(trustedTypes.isHTML(trustedTypes.emptyHTML)); + assert_equals(trustedTypes.emptyHTML.toString(), ""); + }, 'trustedTypes.emptyHTML cannot be redefined via defineProperty.'); + + test(t => { + const empty = trustedTypes.emptyScript; + assert_true(trustedTypes.isScript(empty)); + assert_equals(empty.toString(), ""); + }, 'trustedTypes.emptyScript returns the intended value.'); + + test(t => { + try { trustedTypes.emptyScript = 'fake'; } catch { } + assert_true(trustedTypes.isScript(trustedTypes.emptyScript)); + assert_equals(trustedTypes.emptyScript.toString(), ""); + }, 'trustedTypes.emptyScript cannot be redefined.'); + + test(t => { + try { Object.defineProperty(TrustedTypes, 'emptyScript', 'fake'); } catch { } + assert_true(trustedTypes.isScript(trustedTypes.emptyScript)); + assert_equals(trustedTypes.emptyScript.toString(), ""); + }, 'trustedTypes.emptyScript cannot be redefined via defineProperty.'); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.html new file mode 100644 index 0000000000..746d89fa3f --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-createXYZTests.html @@ -0,0 +1,241 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + //HTML tests + function createHTMLTest(policyName, policy, expectedHTML, t) { + let p = window.trustedTypes.createPolicy(policyName, policy); + let html = p.createHTML('whatever'); + assert_true(html instanceof TrustedHTML); + assert_true(trustedTypes.isHTML(html)); + assert_equals(html + "", expectedHTML); + } + + test(t => { + createHTMLTest('TestPolicyHTML1', { createHTML: s => s }, 'whatever', t); + }, "html = identity function"); + + test(t => { + createHTMLTest('TestPolicyHTML2', { createHTML: s => null }, "", t); + }, "html = null"); + + var HTMLstr = 'well, '; + test(t => { + createHTMLTest('TestPolicyHTML3', { createHTML: s => HTMLstr + s }, HTMLstr + 'whatever', t); + }, "html = string + global string"); + + var HTMLx = 'global'; + test(t => { + createHTMLTest('TestPolicyHTML4', { createHTML: s => { HTMLx = s; return s; } }, 'whatever', t); + assert_equals(HTMLx, 'whatever'); + }, "html = identity function, global string changed"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyHTML5', { createHTML: s => { throw new Error(); }}); + assert_throws_js(Error, _ => { + p.createHTML('whatever'); + }); + }, "html = callback that throws"); + + var obj = { + "foo": "well," + } + + function getHTML(s) { + return this.foo + " " + s; + } + + test(t => { + createHTMLTest('TestPolicyHTML6', { createHTML: getHTML.bind(obj) }, 'well, whatever', t); + }, "html = this bound to an object"); + + var foo = "well,"; + test(t => { + createHTMLTest('TestPolicyHTML7', { createHTML: s => getHTML(s) }, 'well, whatever', t); + }, "html = this without bind"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyHTML8', null); + assert_throws_js(TypeError, _ => { + p.createHTML('whatever'); + }); + }, "html - calling undefined callback throws"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyHTML9', { createHTML: createHTMLJS }); + assert_throws_js(TypeError, _ => { + p.createScript(INPUTS.SCRIPT); + }); + assert_throws_js(TypeError, _ => { + p.createScriptURL(INPUTS.SCRIPTURL); + }); + assert_throws_js(TypeError, _ => { + p.createURL(INPUTS.URL); + }); + }, "createHTML defined - calling undefined callbacks throws"); + + //Script tests + function createScriptTest(policyName, policy, expectedScript, t) { + let p = window.trustedTypes.createPolicy(policyName, policy); + let script = p.createScript('whatever'); + assert_true(script instanceof TrustedScript); + assert_true(trustedTypes.isScript(script)); + assert_equals(script + "", expectedScript); + } + + test(t => { + createScriptTest('TestPolicyScript1', { createScript: s => s }, 'whatever', t); + }, "script = identity function"); + + test(t => { + createScriptTest('TestPolicyScript2', { createScript: s => null }, "", t); + }, "script = null"); + + var Scriptstr = 'well, '; + test(t => { + createScriptTest('TestPolicyScript3', { createScript: s => Scriptstr + s }, Scriptstr + 'whatever', t); + }, "script = string + global string"); + + var Scriptx = 'global'; + test(t => { + createScriptTest('TestPolicyScript4', { createScript: s => { Scriptx = s; return s; } }, 'whatever', t); + assert_equals(Scriptx, 'whatever'); + }, "script = identity function, global string changed"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScript5', { + createScript: s => { throw new Error(); } + }); + assert_throws_js(Error, _ => { + p.createScript('whatever'); + }); + }, "script = callback that throws"); + + var obj = { + "foo": "well," + } + + function getScript(s) { + return this.foo + " " + s; + } + + test(t => { + createScriptTest('TestPolicyScript6', { createScript: getScript.bind(obj) }, 'well, whatever', t); + }, "script = this bound to an object"); + + var foo = "well,"; + test(t => { + createScriptTest('TestPolicyScript7', { createScript: s => getScript(s) }, 'well, whatever', t); + }, "script = this without bind"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScript8', null); + assert_throws_js(TypeError, _ => { + p.createScript('whatever'); + }); + }, "script - calling undefined callback throws"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScript9', { createScript: createScriptJS }); + assert_throws_js(TypeError, _ => { + p.createHTML(INPUTS.HTML); + }); + assert_throws_js(TypeError, _ => { + p.createScriptURL(INPUTS.SCRIPTURL); + }); + assert_throws_js(TypeError, _ => { + p.createURL(INPUTS.URL); + }); + }, "createScript defined - calling undefined callbacks throws"); + + + //ScriptURL tests + function createScriptURLTest(policyName, policy, expectedScriptURL, t) { + let p = window.trustedTypes.createPolicy(policyName, policy); + let scriptUrl = p.createScriptURL(INPUTS.SCRIPTURL); + assert_true(scriptUrl instanceof TrustedScriptURL); + assert_true(trustedTypes.isScriptURL(scriptUrl)); + assert_equals(scriptUrl + "", expectedScriptURL); + } + + test(t => { + createScriptURLTest('TestPolicyScriptURL1', { createScriptURL: s => s }, INPUTS.SCRIPTURL, t); + }, "script_url = identity function"); + + test(t => { + createScriptURLTest('TestPolicyScriptURL2', { createScriptURL: s => null }, "", t); + }, "script_url = null"); + + var scriptURLstr = '#duck'; + test(t => { + createScriptURLTest('TestPolicyScriptURL3', { createScriptURL: s => s + scriptURLstr }, INPUTS.SCRIPTURL + scriptURLstr, t); + }, "script_url = string + global string"); + + var scriptURLx = 'global'; + test(t => { + createScriptURLTest('TestPolicyScriptURL4', { createScriptURL: s => { ScriptURLx = s; return s; } }, INPUTS.SCRIPTURL, t); + assert_equals(ScriptURLx, INPUTS.SCRIPTURL); + }, "script_url = identity function, global string changed"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScriptURL5', { + createScriptURL: s => { throw new Error(); } + }); + assert_throws_js(Error, _ => { + p.createScriptURL(INPUTS.SCRIPTURL); + }); + }, "script_url = callback that throws"); + + function getScriptURL(s) { + return s + this.baz; + } + + var obj = { + "baz": "#duck" + } + + test(t => { + createScriptURLTest('TestPolicyScriptURL6', { createScriptURL: getScriptURL.bind(obj) }, INPUTS.SCRIPTURL + "#duck", t); + }, "script_url = this bound to an object"); + + var baz = "#duck"; + test(t => { + createScriptURLTest('TestPolicyScriptURL7', { createScriptURL: s => getScriptURL(s) }, INPUTS.SCRIPTURL + baz, t); + }, "script_url = this without bind"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScriptURL8', null); + assert_throws_js(TypeError, _ => { + p.createScriptURL(INPUTS.SCRIPTURL); + }); + }, "script_url - calling undefined callback throws"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScriptURL9', { createScriptURL: createScriptURLJS }); + assert_throws_js(TypeError, _ => { + p.createHTML(INPUTS.HTML); + }); + assert_throws_js(TypeError, _ => { + p.createScript(INPUTS.SCRIPT); + }); + assert_throws_js(TypeError, _ => { + p.createURL(INPUTS.URL); + }); + }, "createScriptURL defined - calling undefined callbacks throws"); + + test(t => { + let p = window.trustedTypes.createPolicy('TestPolicyScriptURL10', { + createHTML: createHTMLJSWithThreeArguments, + createScript: createScriptJSWithThreeArguments, + createScriptURL: createScriptURLJSWithThreeArguments + }); + assert_equals("abc", p.createHTML("a", "b", "c").toString()); + assert_equals("abc", p.createScript("a", "b", "c").toString()); + assert_equals("abc", p.createScriptURL("a", "b", "c").toString()); + assert_equals("abundefined", p.createHTML("a", "b").toString()); + assert_equals("a123null", p.createScript("a", 123, null).toString()); + assert_equals("a[object Object]3.14", p.createScriptURL("a", {}, 3.14).toString()); + }, "Arbitrary number of arguments"); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-noNamesGiven.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-noNamesGiven.html new file mode 100644 index 0000000000..15728b9888 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-noNamesGiven.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" content="trusted-types"> +<body> +<script> + //No name given test + test(t => { + assert_throws_js(TypeError, _ => { + window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + }); + }, "No name list given - policy creation throws"); +</script> + diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none-skip.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none-skip.html new file mode 100644 index 0000000000..d0920497d9 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none-skip.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" content="trusted-types * 'none' 'allow-duplicates'"> +<body> +<script> + test(t => { + window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + }, "Can create policy with name 'SomeName'"); + + test(t => { + window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + }, "Can create a second policy with name 'SomeName'"); + + test(t => { + window.trustedTypes.createPolicy('default', { createHTML: s => s } ); + }, "Can create policy with name 'default'"); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html new file mode 100644 index 0000000000..48c75937ea --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" content="trusted-types 'none'"> +<body> +<script> + test(t => { + assert_throws_js(TypeError, _ => { + window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + }); + }, "Cannot create policy with name 'SomeName' - policy creation throws"); + + test(t => { + assert_throws_js(TypeError, _ => { + window.trustedTypes.createPolicy('default', { createHTML: s => s } ); + }); + }, "Cannot create policy with name 'default' - policy creation throws"); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-wildcard.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-wildcard.html new file mode 100644 index 0000000000..9200708545 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-wildcard.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + //No name given test + test(t => { + let policy = window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + assert_equals(policy.name, 'SomeName'); + }, "Wildcard given - policy creation works"); +</script> + diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests.html new file mode 100644 index 0000000000..3a56546151 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="trusted-types SomeName JustOneMoreName"> +<body> +<script> + // Allowed name test + test(t => { + let policy = window.trustedTypes.createPolicy('SomeName', { createHTML: s => s } ); + assert_equals(policy.name, 'SomeName'); + }, "Allowed-name policy creation works."); + + // Another allowed name test + test(t => { + let policy = window.trustedTypes.createPolicy('JustOneMoreName', { createHTML: s => s } ); + assert_equals(policy.name, 'JustOneMoreName'); + }, "Another allowed-name policy creation works."); + + // Non-allowed names test + test(t => { + assert_throws_js(TypeError, _ => { + window.trustedTypes.createPolicy('SomeOtherName', { createURL: s => s } ); + }); + }, "Non-allowed name policy creation throws."); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-nameTests.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-nameTests.html new file mode 100644 index 0000000000..9fdafb2ccf --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-nameTests.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<meta http-equiv="Content-Security-Policy" + content="trusted-types SomeName SomeOtherName"> +<body> +<script> + // Policy name test + test(t => { + let policy = trustedTypes.createPolicy('SomeName', {} ); + assert_true(policy instanceof TrustedTypePolicy); + assert_equals(policy.name, 'SomeName'); + }, "policy.name = name"); + + // Duplicate names test + test(t => { + assert_throws_js(TypeError, _ => { + trustedTypes.createPolicy('SomeName', {} ); + }); + }, "duplicate policy name attempt throws"); + + // Check error messages. + test(t => { + try { + trustedTypes.createPolicy("unknown name", {}); + } catch (e) { + assert_true(e.toString().includes("disallowed")); + assert_false(e.toString().includes("already exists")); + } + + try { + trustedTypes.createPolicy("SomeName", {}); + } catch (e) { + assert_false(e.toString().includes("disallowed")); + assert_true(e.toString().includes("already exists")); + } + }, "Error messages for duplicates and unlisted policies should be different"); + +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-unenforced.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-unenforced.html new file mode 100644 index 0000000000..2934448202 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-createPolicy-unenforced.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + // This test tests error handling of duplicate policy creation when no + // Trusted Types enforcement is active. + + test(t => { + trustedTypes.createPolicy('foo', {} ); + trustedTypes.createPolicy('foo', {} ); + }, "Duplicate policy names should be tolerated (unless in enforcing mode)"); + +</script> +</body> + diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-defaultPolicy.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-defaultPolicy.html new file mode 100644 index 0000000000..7ac09d8bb9 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-defaultPolicy.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + test(t => { + assert_equals(window.trustedTypes.defaultPolicy, null); + }, "defaultPolicy with no default created is not an error"); + + test(t => { + let policy = window.trustedTypes.createPolicy('default', { createHTML: s => s } ); + assert_true(policy instanceof TrustedTypePolicy); + assert_equals(policy, window.trustedTypes.defaultPolicy); + }, "defaultPolicy returns the correct default policy"); + + test(t => { + let foo_policy = window.trustedTypes.createPolicy('foo', { createHTML: s => s } ); + let default_policy = window.trustedTypes.defaultPolicy; + window.trustedTypes.defaultPolicy = foo_policy; + assert_equals(window.trustedTypes.defaultPolicy, default_policy); + assert_not_equals(window.trustedTypes.defaultPolicy, foo_policy); + }, "defaultPolicy is a read-only property"); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getAttributeType-namespace.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getAttributeType-namespace.html new file mode 100644 index 0000000000..0f0d820e28 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getAttributeType-namespace.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +// Tests for getAttributeType with namespaces, based on +// https://crbug.com/1305293 (attachment on comment 0). + +const parser = new DOMParser(); +[ + parser.parseFromString( + '<!doctype html><html><head><script src="./foo.js"><'+'/script></head></html>', + 'text/html'), + parser.parseFromString( + '<html xmlns="http://www.w3.org/1999/xhtml"><head><script src="./foo.js" /></head></html>', + 'application/xhtml+xml'), + parser.parseFromString( + '<xml xmlns:html="http://www.w3.org/1999/xhtml"><html:script src="./foo.js" /></xml>', + 'application/xml') +].forEach((doc, index) => { + const element = doc.getElementsByTagNameNS("http://www.w3.org/1999/xhtml", 'script')[0]; + const attribute = element.getAttributeNode("src"); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, element.namespaceURI, attribute.namespaceURI), + "TrustedScriptURL"); + }, `${index}: getAttributeType with full namespace info.`); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, element.namespaceURI, undefined), + "TrustedScriptURL"); + },`${index}: getAttributeType with element namespace and empty attribute namespace`); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, undefined, undefined), "TrustedScriptURL"); + }, `${index}: getAttributeType without namespaces.`); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, undefined, ""), "TrustedScriptURL"); + }, `${index}: getAttributeType with undefined and empty namespace.`); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, "", undefined), "TrustedScriptURL"); + }, `${index}: getAttributeType with empty and undefined namespace.`); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, "", ""), "TrustedScriptURL"); + }, `${index}: getAttributeType with empty namespaces.`); + + test(_ => { + assert_equals(trustedTypes.getAttributeType( + element.localName, attribute.name, element.namespaceURI, ""), "TrustedScriptURL"); + }, `${index}: getAttributeType with element namespace and empty attribute namespace.`); +}); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html new file mode 100644 index 0000000000..21aba668cc --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<body> +<div id="target"></div> +<script> + test(t => { + assert_equals(trustedTypes.getPropertyType("script", "text"), "TrustedScript"); + assert_equals(trustedTypes.getPropertyType("script", "src"), "TrustedScriptURL"); + assert_equals(trustedTypes.getPropertyType("script", "id"), null); + assert_equals(trustedTypes.getPropertyType("script", "b"), null); + }, "sanity check trustedTypes.getPropertyType for the HTML script element."); + + test(t => { + assert_equals(trustedTypes.getAttributeType("img", "onerror"), "TrustedScript"); + assert_equals(trustedTypes.getAttributeType("img", "width"), null); + assert_equals(trustedTypes.getAttributeType("img", "madeup"), null); + }, "sanity check trustedTypes.getAttributeType."); + + test(t => { + assert_true(!!trustedTypes.getTypeMapping()); + }, "sanity check trustedTypes.getTypeMapping"); + + + // getPropertyType tests adapted from w3c/trusted-types polyfill: + test(t => { + // returns the proper type for attribute-related properties + assert_equals(trustedTypes.getPropertyType("script", "src"), "TrustedScriptURL"); + + // is case insensitive for tag names + assert_equals(trustedTypes.getPropertyType("SCRIPT", "src"), "TrustedScriptURL"); + + // is case sensitive for property names + assert_equals(trustedTypes.getPropertyType("script", "sRc"), null); + assert_equals(trustedTypes.getPropertyType("div", "innerhtml"), null); + + // returns the proper type for innerHTML + assert_equals(trustedTypes.getPropertyType("div", "innerHTML"), "TrustedHTML"); + + // returns the proper type for outerHTML + assert_equals(trustedTypes.getPropertyType("div", "outerHTML"), "TrustedHTML"); + + // returns the proper type for script.prop + ["text", "innerText", "textContent"].forEach((prop) => { + assert_equals(trustedTypes.getPropertyType("script", prop), "TrustedScript"); + }); + }, "getPropertyType tests adapted from w3c/trusted-types polyfill"); + + test(t => { + // returns the proper type + assert_equals(trustedTypes.getAttributeType('script', 'src'), 'TrustedScriptURL'); + + // ignores attributes from unknown namespaces + assert_equals(trustedTypes.getAttributeType( + 'a', 'href', '', 'http://foo.bar'), null); + + // is case insensitive for element names + assert_equals(trustedTypes.getAttributeType('SCRIPT', 'src'), 'TrustedScriptURL'); + + // is case insensitive for the attribute names + assert_equals(trustedTypes.getAttributeType('script', 'SRC'), 'TrustedScriptURL'); + + // supports the inline event handlers + assert_equals(trustedTypes.getAttributeType('img', 'onerror'), 'TrustedScript'); + assert_equals(trustedTypes.getAttributeType('unknown', 'onerror'), 'TrustedScript'); + + // defaults to undefined + assert_equals(trustedTypes.getAttributeType('unknown', 'src'), null); + assert_equals(trustedTypes.getAttributeType('img', 'bar'), null); + }, "getAttributeType tests adapted from w3c/trusted-types polyfill"); + + + test(t=> { + const map = trustedTypes.getTypeMapping(); + + // Spot testing some values. + assert_equals(map["script"].attributes.src, "TrustedScriptURL"); + assert_equals(map["*"].properties.innerHTML, "TrustedHTML"); + assert_equals(map["foo"], undefined); + + // getTypeMapping returns a 'clean' object, in case the return value has + // been modified. + map["*"].attributes["foo"] = "bar"; + assert_equals(trustedTypes.getTypeMapping()["*"].attributes["foo"], undefined); +; + // Unknown namespaces: + assert_equals(trustedTypes.getTypeMapping("http://foo/bar"), null); + }, "getTypeMapping tests adapted from w3c/trusted-types polyfill"); + + // Test case handling for both attributes and properties. + for (let attr of ["codeBase", "CODEBASE", "codebase"]) { + for (let elem of ["object", "OBJECT", "oBjEcT"]) { + test(t => { + // attributes are case-insensitive, so all variants should be defined. + assert_true(trustedTypes.getAttributeType(elem, attr) != undefined); + }, `${elem}[${attr}] is defined`); + test(t => { + // properties are case-sensitive, so only the "correct" spelling + // should be defined. + assert_equals(trustedTypes.getPropertyType(elem, attr) != undefined, + attr == "codeBase"); + }, `${elem}.${attr} is maybe defined`); + } + } +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-isXXX.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-isXXX.html new file mode 100644 index 0000000000..a7df477d00 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-isXXX.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<body> +<script> + // Policy settings for all tests + const noopPolicy = { + 'createHTML': (s) => s, + 'createScriptURL': (s) => s, + 'createScript': (s) => s, + }; + + // isHTML tests + test(t => { + const p = trustedTypes.createPolicy('html', noopPolicy); + let html = p.createHTML(INPUTS.HTML); + + assert_true(trustedTypes.isHTML(html)); + let html2 = Object.create(html); + + // instanceof can pass, but we rely on isHTML + assert_true(html2 instanceof TrustedHTML); + assert_false(trustedTypes.isHTML(html2)); + + let html3 = Object.assign({}, html, {toString: () => 'fake'}); + + assert_false(trustedTypes.isHTML(html3)); + }, 'TrustedTypePolicyFactory.isHTML requires the object to be created via policy.'); + + // isScript tests + test(t => { + const p = trustedTypes.createPolicy('script', noopPolicy); + let script = p.createScript(INPUTS.SCRIPT); + + assert_true(trustedTypes.isScript(script)); + let script2 = Object.create(script); + + // instanceof can pass, but we rely on isScript + assert_true(script2 instanceof TrustedScript); + assert_false(trustedTypes.isScript(script2)); + + let script3 = Object.assign({}, script, {toString: () => 'fake'}); + + assert_false(trustedTypes.isScript(script3)); + }, 'TrustedTypePolicyFactory.isScript requires the object to be created via policy.'); + + // isScriptURL tests + test(t => { + const p = trustedTypes.createPolicy('script_url', noopPolicy); + let script = p.createScriptURL(INPUTS.SCRIPTURL); + + assert_true(trustedTypes.isScriptURL(script)); + let script2 = Object.create(script); + + // instanceof can pass, but we rely on isScript + assert_true(script2 instanceof TrustedScriptURL); + assert_false(trustedTypes.isScriptURL(script2)); + + let script3 = Object.assign({}, script, {toString: () => 'fake'}); + + assert_false(trustedTypes.isScriptURL(script3)); + }, 'TrustedTypePolicyFactory.isScriptURL requires the object to be created via policy.'); + + // Test non-object parameters. + test(t => { + assert_false(trustedTypes.isHTML(null)); + assert_false(trustedTypes.isHTML(123)); + assert_false(trustedTypes.isHTML(0.5)); + assert_false(trustedTypes.isHTML('test')); + assert_false(trustedTypes.isHTML({})); + assert_false(trustedTypes.isScript(null)); + assert_false(trustedTypes.isScript(123)); + assert_false(trustedTypes.isScript(0.5)); + assert_false(trustedTypes.isScript('test')); + assert_false(trustedTypes.isScript({})); + assert_false(trustedTypes.isScriptURL(null)); + assert_false(trustedTypes.isScriptURL(123)); + assert_false(trustedTypes.isScriptURL(0.5)); + assert_false(trustedTypes.isScriptURL('test')); + assert_false(trustedTypes.isScriptURL({})); + }, 'TrustedTypePolicyFactory.isXXX should accept anything without throwing.'); +</script> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-metadata.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-metadata.html new file mode 100644 index 0000000000..70a5b44666 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-metadata.html @@ -0,0 +1,154 @@ +<!DOCTYPE html> +<meta name="timeout" content="long"> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +<body> +<div id="target"></div> +<script> + const policy = trustedTypes.createPolicy("anythinggoes", { + "createHTML": x => x, + "createScript": x => x, + "createScriptURL": x => x, + }); + + const create_value = { + "TrustedHTML": policy.createHTML("hello"), + "TrustedScript": policy.createScript("x => x + x"), + "TrustedScriptURL": policy.createScriptURL("https://url.invalid/blubb.js"), + null: "empty", + }; + + // The tests will assign all values to all sink types. To prevent spurious + // errors when assigning "hello" to a script sink, we'll just define a + // varaible of that name. + const hello = ""; + + // We seed our elements and properties with one element/property that has no + // 'trusted type' sinks, and one non-specced (madeup) element/property. + // Also add several event handlers (onclick). + let elements = ['madeup', 'b']; + let properties = ['madeup', 'id', "onerror", "onclick"]; + const types = [null, "TrustedHTML", "TrustedScript", "TrustedScriptURL"]; + + // We'll wrap construction of the elements/properties list in a test, mainly + // so we'll get decent error messages when it might fail. + test(t => { + // Collect all element and property names from getTypeMapping(). + const map = trustedTypes.getTypeMapping(); + for (let elem in map) { + elements.push(elem); + properties = properties.concat(Object.keys(map[elem].properties)); + } + + // Remove "*" entries and de-duplicate properties. + elements = elements.filter(s => !s.includes("*")); + properties = properties.filter(s => !s.includes("*")); + properties = Array.from(new Set(properties)); + }, "Prepare parameters for generic test series below."); + + // Iterate one test for each combination of element, property, and sink type. + const target = document.getElementById("target"); + for (elem of elements) { + for (property of properties) { + for (type of types) { + // Conceptually, our test is beautifully simple: Ask getPropertyType + // about the expected trusted type. If it's the one we're testing, + // expect the assignment to pass, and expect we can read back the same + // value. If it's a different type, expect the assignment to fail. + // + // The idealized test case would look something like this: + // const value = ....; + // const test_fn = _ => { element[property] = value }; + // if (type == expected_type || !expected_type) { + // test_fn(); + // assert_equals(element[property], value); + // } else { + // assert_throws_js(..., test_fn, ...); + // } + // + // Below is the same logic, but extended to handle the various edge + // cases. + // + // Some difficulties we need to accomodate: + // - Some properties have built-in behaviours. (E.g. the "innerHTML" + // value depends on whether elements are visible or not.) To + // accomodate this, we have a big switch-statement that handles + // those types of exceptions. + // - Some properties parse their values, so that you can't easily get + // the original text value back (e.g. error handlers). + // - Some properties cause actions as side-effects (e.g. loading a + // script), which will cause errors in the test (despite the actual + // code-under-test meeting our expectations). This is handled with + // a cleanup script which removes the element (and thus cancels + // the loading actions). + test(t => { + const element = target.appendChild(document.createElement(elem)); + t.add_cleanup(_ => element.remove()); + const expected_type = trustedTypes.getPropertyType(elem, property); + const value = create_value[type]; + const test_fn = _ => { element[property] = value; }; + if (type == expected_type || !expected_type) { + test_fn(); + let result_value = element[property]; + switch (property) { + // Node.innerText returns the rendered text of an Element, which + // in this test case is usually empty. For this specific case, + // let's just check "textContent" instead. + // Node.innerHTML re-creates a text representation from the DOM, + // which may not always match the exact input. + case "innerText": + case "innerHTML": + result_value = element["textContent"]; + break; + // Node.outerHTML replaces the node, which means the simple + // checking logic below does not work. In this case, we'll just + // return and hence skip the result comparison. + case "outerHTML": + return; + // URL-typed accessors + case "src": + if (elem == "iframe") + return; + break; + // Properties starting with "on" are usually error handlers, + // which will parse their input as a function. In this case, + // also skip the result comparison. + default: + if (property.startsWith("on")) return; + break; + } + assert_equals("" + result_value, "" + value); + } else { + assert_throws_js(TypeError, test_fn, "throws"); + } + }, `Test assignment of ${type ? type : "string"} on ${elem}.${property}`); + + // And once more, for attributes. + test(t => { + const element = target.appendChild(document.createElement(elem)); + t.add_cleanup(_ => element.remove()); + const expected_type = trustedTypes.getAttributeType(elem, property); + const value = create_value[type]; + const test_fn = _ => { element.setAttribute(property, value); }; + if (type == expected_type || !expected_type) { + test_fn(); + assert_equals("" + element.getAttribute(property), "" + value); + } else if (elem == "script" && (property == "innerText" || + property == "textContent" || property == "text")) { + // Due to the "slot setting" mechnanism, setting script's innerText, + // textContent and text attributes dosn't throw. So we can't use + // that in our tests. + // ref: https://w3c.github.io/trusted-types/dist/spec/#setting-slot-values + test_fn(); + } else { + assert_throws_js(TypeError, test_fn, "throws"); + } + }, `Test assignment of ${type ? type : "string"} on ${elem}.setAttribute(${property},..)`); + } + } + } +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/Window-TrustedTypes.html b/testing/web-platform/tests/trusted-types/Window-TrustedTypes.html new file mode 100644 index 0000000000..718c763afa --- /dev/null +++ b/testing/web-platform/tests/trusted-types/Window-TrustedTypes.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> +<body> +<script> + test(t => { + let factory = window.trustedTypes; + assert_true(factory instanceof TrustedTypePolicyFactory); + }, "factory = window.trustedTypes"); + + test(t => { + assert_throws_js(TypeError, _ => { + let factory = new TrustedTypePolicyFactory(); + }); + }, "factory construction fails"); +</script> diff --git a/testing/web-platform/tests/trusted-types/WorkerGlobalScope-eval.html b/testing/web-platform/tests/trusted-types/WorkerGlobalScope-eval.html new file mode 100644 index 0000000000..9248784924 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/WorkerGlobalScope-eval.html @@ -0,0 +1,42 @@ +<!doctype html> +<html> +<head> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id=log></div> + +<script> + +// To test workers, we need to importScripts source files in the workers. +// Since the point of this test is to test blocking of importScripts, we need +// to set up one policy that will blindly pass through URLs for use in the test +// setup, and then have additional policies for the actual test cases. +// +// For the same reason we cannot use the otherwise preferred 'META: workers' +// tag, since that test setup would be blocked as soon as trusted types +// enforcement is enabled. +const test_setup_policy = trustedTypes.createPolicy("hurrayanythinggoes", { + createScriptURL: x => x}); +const test_url = + test_setup_policy.createScriptURL("support/WorkerGlobalScope-eval.https.js"); + +fetch_tests_from_worker(new Worker(test_url)); + +fetch_tests_from_worker(new SharedWorker(test_url)); + +// Cargo-culted from code generated from "META: worker". +if ('serviceWorker' in navigator) { + (async function() { + const scope = 'support/some/scope/for/this/test'; + let reg = await navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + reg = await navigator.serviceWorker.register(test_url, {scope}); + fetch_tests_from_worker(reg.installing); + })(); +} + +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/WorkerGlobalScope-importScripts.html b/testing/web-platform/tests/trusted-types/WorkerGlobalScope-importScripts.html new file mode 100644 index 0000000000..9853b1bc44 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/WorkerGlobalScope-importScripts.html @@ -0,0 +1,42 @@ +<!doctype html> +<html> +<head> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id=log></div> + +<script> + +// To test workers, we need to importScripts source files in the workers. +// Since the point of this test is to test blocking of importScripts, we need +// to set up one policy that will blindly pass through URLs for use in the test +// setup, and then have additional policies for the actual test cases. +// +// For the same reason we cannot use the otherwise preferred 'META: workers' +// tag, since that test setup would be blocked as soon as trusted types +// enforcement is enabled. +const test_setup_policy = trustedTypes.createPolicy("hurrayanythinggoes", { + createScriptURL: x => x}); +const test_url = + test_setup_policy.createScriptURL("support/WorkerGlobalScope-importScripts.https.js"); + +fetch_tests_from_worker(new Worker(test_url)); + +fetch_tests_from_worker(new SharedWorker(test_url)); + +// Cargo-culted from code generated from "META: worker". +if ('serviceWorker' in navigator) { + (async function() { + const scope = 'support/some/scope/for/this/test'; + let reg = await navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + reg = await navigator.serviceWorker.register(test_url, {scope}); + fetch_tests_from_worker(reg.installing); + })(); +} + +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/block-Document-execCommand.html b/testing/web-platform/tests/trusted-types/block-Document-execCommand.html new file mode 100644 index 0000000000..87759cdc19 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-Document-execCommand.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'"> +<link rel="author" title="Daniel Vogelheim" href="mailto:vogelheim@chromium.org"></link> +<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/"></link> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + // Tests that certain execCommand commands will observe Trusted Types if + // it's enforced. + const commands = [ "insertHTML", "paste" ]; + const tt_commands = [ "insertHTML" ]; + + // A pass-through policy for testing. + const a_policy = trustedTypes.createPolicy("a policy", {"createHTML": x => x}); + + for (const command of commands) { + const requires_tt = tt_commands.includes(command); + + // Test that execCommand with String throws, but only for commands that + // require TT. + if (requires_tt) { + test(t => { + assert_throws_js(TypeError, _ => document.execCommand(command, false, "<em>Hello World</em>")); + }, `Document.execCommand("${command}") throws.`); + } else { + test(t => { + document.execCommand(command, false, "<em>Hello World</em>"); + }, `Document.execCommand("${command}") works as usual."`); + } + // Test that execCommand succeeds with a TrustedHTML argument. + test(t => { + document.execCommand(command, false, a_policy.createHTML("<em>Hello World</em>")); + }, `Document.execCommand("${command}") works with a TrustedHTML argument.`); + } + + // Test that with a default policy, all comamnds will work again. + trustedTypes.createPolicy("default", {"createHTML": x => x}); + for (const command of commands) { + test(t => { + document.execCommand(command, false, "<em>Hello World</em>"); + }, `Document.execCommand("${command}") works as usual with a default policy.`); + } +</script> diff --git a/testing/web-platform/tests/trusted-types/block-Node-multiple-arguments.html b/testing/web-platform/tests/trusted-types/block-Node-multiple-arguments.html new file mode 100644 index 0000000000..c3e7671534 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-Node-multiple-arguments.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<div id="container"></div> +<script> + 'use strict'; + const container = document.querySelector("#container"); + const policy = window.trustedTypes.createPolicy("policy", { + createScript: t => t, + }); + function stringify(arg) { + return "textContent" in Object.getPrototypeOf(arg) ? arg.textContent : arg.toString() + } + + // Test all combinations of: + // - DOM methods: append, prepend, replaceWith, after, before + // - one or two parameters, string, Text node, Trusted Script + // - into regular container or <script> element. + // + // Test arguments are all string literals with a semicolon, because - + // depending on target - it might be injected into a <script> and shouldn't + // cause syntax errors there. + const targets = ["div", "script"]; + const pass_args = [ + [ policy.createScript("'createScript';") ], + [ policy.createScript("'createScript #1';"), policy.createScript("'#2;'") ], + ]; + const fail_args = [ + [ "'plain text';" ], + [ "'plain text #1';", "'plain text #2';" ], + [ document.createTextNode("'node';") ], + [ document.createTextNode("'node #1';"), + document.createTextNode("'node #2';") ], + [ "'mixed';", document.createTextNode("'node';") ], + [ "'mixed';", policy.createScript("'script';") ], + [ document.createTextNode("'node';"), + policy.createScript("'script';") ], + ]; + const all_args = [].concat(pass_args).concat(fail_args); + + for (const target of targets) { + for (const args of all_args) { + var should_fail = target == "script" && fail_args.indexOf(args) != -1; + var fail_string = should_fail ? "fail" : "pass"; + + for (const setter of [container.replaceWith, container.after, container.before]) { + test(t => { + var outer = document.createElement(target); + container.appendChild(outer); + var inner = document.createElement("p"); + outer.appendChild(inner); + var test_fn = _ => { setter.apply(inner, args); }; + var expected; + if (should_fail) { + assert_throws_js(TypeError, test_fn, "This should throw."); + expected = ""; + } else { + test_fn(); + expected = args.map(stringify).join(""); + } + assert_equals(outer.textContent, expected); + }, `${setter.name}(${args.toString()}) on <${target}> should ${fail_string}`); + } + + for (const setter of [container.append, container.prepend]) { + test(t => { + let outer = document.createElement(target); + container.appendChild(outer); + var test_fn = _ => { setter.apply(outer, args); }; + var expected; + if (should_fail) { + assert_throws_js(TypeError, test_fn, "This should throw."); + expected = ""; + } else { + test_fn(); + expected = args.map(stringify).join(""); + } + assert_equals(outer.textContent, expected); + }, `${setter.name}(${args.toString()}) on <${target}> should ${fail_string}`); + } + } + } +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-DOMParser-parseFromString.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-DOMParser-parseFromString.html new file mode 100644 index 0000000000..dd912e6b8d --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-DOMParser-parseFromString.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +<body> +<script> + // Trusted HTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let parser = new DOMParser(); + let doc = parser.parseFromString(html, "text/html"); + assert_equals(doc.body.innerText, RESULTS.HTML); + }, "document.innerText assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + var parser = new DOMParser(); + assert_throws_js(TypeError, _ => { + var doc = parser.parseFromString("Fail", "text/html"); + }); + }, "`document.innerText = string` throws."); + + // Null assignment throws. + test(t => { + var parser = new DOMParser(); + assert_throws_js(TypeError, _ => { + var doc = parser.parseFromString(null, "text/html"); + }); + }, "'document.innerText = null' throws"); + + // After default policy creation string assignment implicitly calls createHTML. + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + let parser = new DOMParser(); + let doc = parser.parseFromString(INPUTS.HTML, "text/html"); + assert_equals(doc.body.innerText, RESULTS.HTML); + }, "'document.innerText = string' assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + var parser = new DOMParser(); + var doc = parser.parseFromString(null, "text/html"); + assert_equals(doc.body.innerText, "null"); + }, "'document.innerText = null' assigned via default policy does not throw"); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.html new file mode 100644 index 0000000000..c24715718d --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +<body> +<script> + // setTimeout tests + // TrustedScript assignments do not throw. + async_test(t => { + window.timeoutTest = t; + let policy = createScript_policy(window, 'timeout'); + let script = policy.createScript("window.timeoutTest.done();"); + setTimeout(script); + }, "window.setTimeout assigned via policy (successful Script transformation)."); + + // String assignments throw. + test(t => { + window.timeoutTestString = t.unreached_func(); + assert_throws_js(TypeError, _ => { + setTimeout("window.timeoutTestString();"); + }); + }, "`window.setTimeout(string)` throws."); + + // Null assignment throws. + test(t => { + assert_throws_js(TypeError, _ => { + setTimeout(null); + }); + }, "`window.setTimeout(null)` throws."); + + // setInterval tests + // TrustedScript assignments do not throw. + async_test(t => { + window.intervalTest = t; + let policy = createScript_policy(window, 'script'); + let script = policy.createScript("window.intervalTest.done();"); + setInterval(script); + }, "window.setInterval assigned via policy (successful Script transformation)."); + + // String assignments throw. + test(t => { + window.intervalTestString = t.unreached_func(); + assert_throws_js(TypeError, _ => { + setInterval("window.intervalTestString()"); + }); + }, "`window.setInterval(string)` throws."); + + // Null assignment throws. + test(t => { + assert_throws_js(TypeError, _ => { + setInterval(null); + }); + }, "`window.setInterval(null)` throws."); + + let policy = window.trustedTypes.createPolicy("default", { createScript: x => "0" }); + // After default policy creation string assignment implicitly calls createScript. + test(t => { + setTimeout(INPUTS.SCRIPT); + setInterval(INPUTS.SCRIPT); + }, "`setTimeout(string)`, `setInterval(string)` via default policy (successful Script transformation)."); + // After default policy creation null assignment implicitly calls createScript. + test(t => { + setTimeout(null); + setInterval(null); + }, "`setTimeout(null)`, `setInterval(null)` via default policy (successful Script transformation)."); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Document-parseHTMLUnsafe.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Document-parseHTMLUnsafe.html new file mode 100644 index 0000000000..83dfcda5d9 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Document-parseHTMLUnsafe.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<script> + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let doc = Document.parseHTMLUnsafe(html); + assert_equals(doc.body.innerText, RESULTS.HTML); + }, "Document.parseHTMLUnsafe assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + assert_throws_js(TypeError, _ => { + var doc = Document.parseHTMLUnsafe("Fail"); + }); + }, "`Document.parseHTMLUnsafe(string)` throws."); + + // Null assignment throws. + test(t => { + assert_throws_js(TypeError, _ => { + var doc = Document.parseHTMLUnsafe(null); + }); + }, "'Document.parseHTMLUnsafe(null)' throws"); + + // After default policy creation string assignment implicitly calls createHTML. + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + let doc = Document.parseHTMLUnsafe(INPUTS.HTML, "text/html"); + assert_equals(doc.body.innerText, RESULTS.HTML); + }, "'Document.parseHTMLUnsafe(string)' assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + var doc = Document.parseHTMLUnsafe(null, "text/html"); + assert_equals(doc.body.innerText, "null"); + }, "'Document.parseHTMLUnsafe(null)' assigned via default policy does not throw"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Document-write.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Document-write.html new file mode 100644 index 0000000000..974203c113 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Document-write.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<script> + // TrustedURL assignments do not throw. + let p = createHTML_policy(window, 1); + test(t => { + document.body.innerText = ''; + let html = p.createHTML(INPUTS.HTML); + document.write(html); + assert_equals(document.body.innerText, RESULTS.HTML); + }, "document.write with html assigned via policy (successful URL transformation)."); + + // TrustedURL assignments do not throw. (Now for writeln.) + test(t => { + document.body.innerText = ''; + let html = p.createHTML(INPUTS.HTML); + document.writeln(html); + assert_equals(document.body.innerText, RESULTS.HTML); + }, "document.writeln with html assigned via policy (successful URL transformation)."); + + // String assignments throw. + test(t => { + const old = document.body.innerText; + assert_throws_js(TypeError, _ => { + document.write('A string'); + }); + assert_equals(document.body.innerText, old); + }, "`document.write(string)` throws"); + + // String assignments throw. (Now for writeln.) + test(t => { + const old = document.body.innerText; + assert_throws_js(TypeError, _ => { + document.writeln('A string'); + }); + assert_equals(document.body.innerText, old); + }, "`document.writeln(string)` throws"); + + // Null assignment throws. + test(t => { + const old = document.body.innerText; + assert_throws_js(TypeError, _ => { + document.write(null); + }); + assert_equals(document.body.innerText, old); + }, "`document.write(null)` throws"); + + // Null assignment throws. (Now for writeln.) + test(t => { + const old = document.body.innerText; + assert_throws_js(TypeError, _ => { + document.writeln(null); + }); + assert_equals(document.body.innerText, old); + }, "`document.writeln(null)` throws"); + + let default_policy = trustedTypes.createPolicy('default', + { createHTML: createHTMLJS }, true ); + + // Default policy works. + test(t => { + document.body.innerText = ''; + document.write(INPUTS.HTML); + assert_equals(document.body.innerText, RESULTS.HTML); + }, "`document.write(string)` observes default policy"); + + // Default policy works. (Now for writeln.) + test(t => { + document.body.innerText = ''; + document.writeln(INPUTS.HTML); + assert_equals(document.body.innerText, RESULTS.HTML); + }, "`document.writeln(string)` observes default policy"); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html new file mode 100644 index 0000000000..228887d042 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html @@ -0,0 +1,147 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container'); + + // Trusted HTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + let before = 'before'; + let after = 'after'; + let htmlBefore = p.createHTML(before); + let htmlAfter = p.createHTML(after); + + var d = document.createElement('div'); + container.appendChild(d); + + d.insertAdjacentHTML('beforebegin', html); + assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.previousSibling.data, RESULTS.HTML); + + d.insertAdjacentHTML('afterbegin', htmlBefore); + d.insertAdjacentHTML('beforeend', htmlAfter); + assert_equals(d.innerHTML, before + after); + + d.insertAdjacentHTML('afterend', html); + assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.nextSibling.data, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "insertAdjacentHTML with html assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('beforebegin', "<p>Fail</p>"); + }); + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('afterbegin', "<p>Fail</p>"); + }); + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('beforeend', "<p>Fail</p>"); + }); + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('afterend', "<p>Fail</p>"); + }); + + assert_equals(d.previousSibling, null); + assert_equals(d.firstChild, null); + assert_equals(d.lastChild, null); + assert_equals(d.nextSibling, null); + + while (container.firstChild) + container.firstChild.remove(); + }, "`insertAdjacentHTML(string)` throws."); + + // Null assignment throws. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('beforebegin', null); + }); + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('afterbegin', null); + }); + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('beforeend', null); + }); + assert_throws_js(TypeError, _ => { + d.insertAdjacentHTML('afterend', null); + }); + + assert_equals(d.previousSibling, null); + assert_equals(d.firstChild, null); + assert_equals(d.lastChild, null); + assert_equals(d.nextSibling, null); + }, "`insertAdjacentHTML(null)` throws."); + + // After default policy creation string assignment implicitly calls createHTML. + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + + var d = document.createElement('div'); + container.appendChild(d); + + d.insertAdjacentHTML('beforebegin', INPUTS.HTML); + assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.previousSibling.data, RESULTS.HTML); + + d.insertAdjacentHTML('afterbegin', INPUTS.HTML); + assert_equals(d.firstChild.nodeType, Node.TEXT_NODE); + assert_equals(d.firstChild.data, RESULTS.HTML); + + d.insertAdjacentHTML('beforeend', INPUTS.HTML); + assert_equals(d.lastChild.nodeType, Node.TEXT_NODE); + assert_equals(d.lastChild.data, RESULTS.HTML); + + d.insertAdjacentHTML('afterend', INPUTS.HTML); + assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.nextSibling.data, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "`insertAdjacentHTML(string)` assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + + d.insertAdjacentHTML('beforebegin', null); + assert_equals(d.previousSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.previousSibling.data, "null"); + + d.insertAdjacentHTML('afterbegin', null); + assert_equals(d.firstChild.nodeType, Node.TEXT_NODE); + assert_equals(d.firstChild.data, "null"); + + d.insertAdjacentHTML('beforeend', null); + assert_equals(d.lastChild.nodeType, Node.TEXT_NODE); + assert_equals(d.lastChild.data, "null"); + + d.insertAdjacentHTML('afterend', null); + assert_equals(d.nextSibling.nodeType, Node.TEXT_NODE); + assert_equals(d.nextSibling.data, "null"); + + while (container.firstChild) + container.firstChild.remove(); + }, "`insertAdjacentHTML(null)` assigned via default policy does not throw."); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-outerHTML.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-outerHTML.html new file mode 100644 index 0000000000..9b6cf72cb4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-outerHTML.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container') + + // TrustedHTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + + var d = document.createElement('div'); + document.querySelector('#container').appendChild(d); + d.outerHTML = html; + assert_equals(container.innerText, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "outerHTML with html assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + assert_throws_js(TypeError, _ => { + d.outerHTML = "Fail."; + }); + assert_equals(container.innerText, ""); + while (container.firstChild) + container.firstChild.remove(); + }, "`outerHTML = string` throws."); + + // Null assignment throws. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + assert_throws_js(TypeError, _ => { + d.outerHTML = null; + }); + assert_equals(container.innerText, ""); + while (container.firstChild) + container.firstChild.remove(); + }, "`outerHTML = null` throws."); + + // After default policy creation string assignment implicitly calls createHTML. + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + + var d = document.createElement('div'); + document.querySelector('#container').appendChild(d); + d.outerHTML = INPUTS.HTML; + assert_equals(container.innerText, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "`outerHTML = string` assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + d.outerHTML = null; + assert_equals(container.innerText, ""); + + while (container.firstChild) + container.firstChild.remove(); + }, "`outerHTML = null` assigned via default policy does not throw"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setAttribute.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setAttribute.html new file mode 100644 index 0000000000..1d39a804f3 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setAttribute.html @@ -0,0 +1,112 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<script> + const nullPolicy = trustedTypes.createPolicy('NullPolicy', {createScript: s => s}); + + // TrustedScriptURL Assignments + const scriptURLTestCases = [ + [ 'embed', 'src' ], + [ 'object', 'data' ], + [ 'object', 'codeBase' ], + [ 'script', 'src' ] + ]; + + scriptURLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_script_url_explicit_set(window, + c[0] + "-" + c[1], t, c[0], c[1], RESULTS.SCRIPTURL); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], 'A string'); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], null); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], nullPolicy.createScript('script')); + }, c[0] + "." + c[1] + " accepts only TrustedScriptURL"); + }); + + // TrustedHTML Assignments + const HTMLTestCases = [ + [ 'iframe', 'srcdoc' ] + ]; + + HTMLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_html_explicit_set(window, c[0] + "-" + c[1], t, c[0], c[1], RESULTS.HTML); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], 'A string'); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], null); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], nullPolicy.createScript('script')); + }, c[0] + "." + c[1] + " accepts only TrustedHTML"); + }); + + // TrustedScript Assignments + const ScriptTestCases = [ + [ 'div', 'onclick' ] + ]; + + ScriptTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_script_explicit_set(window, c[0] + "-" + c[1], t, c[0], c[1], RESULTS.SCRIPT); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], 'A string'); + assert_throws_no_trusted_type_explicit_set(c[0], c[1], null); + }, c[0] + "." + c[1] + " accepts only TrustedScript"); + }); + + test(t => { + let el = document.createElement('script'); + + assert_throws_js(TypeError, _ => { + el.setAttribute('SrC', INPUTS.URL); + }); + + assert_equals(el.src, ''); + }, "`Script.prototype.setAttribute.SrC = string` throws."); + + // After default policy creation string and null assignments implicitly call createXYZ + let p = window.trustedTypes.createPolicy("default", { createScriptURL: createScriptURLJS, createHTML: createHTMLJS, createScript: createScriptJS }, true); + scriptURLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_type(c[0], c[1], INPUTS.SCRIPTURL, RESULTS.SCRIPTURL); + assert_element_accepts_trusted_type(c[0], c[1], null, window.location.toString().replace(/[^\/]*$/, "null")); + }, c[0] + "." + c[1] + " accepts string and null after default policy was created."); + }); + + HTMLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_type(c[0], c[1], INPUTS.HTML, RESULTS.HTML); + assert_element_accepts_trusted_type(c[0], c[1], null, "null"); + }, c[0] + "." + c[1] + " accepts string and null after default policy was created."); + }); + + ScriptTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_type_explicit_set(c[0], c[1], INPUTS.SCRIPT, RESULTS.SCRIPT); + assert_element_accepts_trusted_type_explicit_set(c[0], c[1], null, "null"); + }, c[0] + "." + c[1] + " accepts string and null after default policy was created."); + }); + + // Other attributes can be assigned with TrustedTypes or strings or null values + test(t => { + assert_element_accepts_non_trusted_type_explicit_set('a', 'rel', 'A string', 'A string'); + }, "a.rel accepts strings"); + + test(t => { + assert_element_accepts_non_trusted_type_explicit_set('a', 'rel', null, 'null'); + }, "a.rel accepts null"); + + test(t => { + let embed = document.createElement('embed'); + let script = document.createElement('script'); + + embed.setAttribute('src', INPUTS.SCRIPTURL); + let attr = embed.getAttributeNode('src'); + embed.removeAttributeNode(attr); + script.setAttributeNode(attr); + + assert_equals(script.getAttribute('src'), RESULTS.SCRIPTURL); + }, "`script.src = setAttributeNode(embed.src)` with string works."); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setAttributeNS.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setAttributeNS.html new file mode 100644 index 0000000000..346e077a66 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setAttributeNS.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<script> + test(t => { + assert_element_accepts_trusted_html_set_ns(window, '0', t, 'a', 'b', RESULTS.HTML); + }, "Element.setAttributeNS assigned via policy (successful HTML transformation)"); + + test(t => { + assert_element_accepts_trusted_script_set_ns(window, '1', t, 'a', 'b', RESULTS.SCRIPT); + }, "Element.setAttributeNS assigned via policy (successful Script transformation)"); + + test(t => { + assert_element_accepts_trusted_script_url_set_ns(window, '2', t, 'a', 'b', RESULTS.SCRIPTURL); + }, "Element.setAttributeNS assigned via policy (successful ScriptURL transformation)"); + + // Unknown, namespaced attributes should not be TT checked: + test(t => { + assert_element_accepts_non_trusted_type_set_ns('a', 'b', 'A string', 'A string'); + }, "Element.setAttributeNS accepts untrusted string for non-specced accessor"); + + test(t => { + assert_element_accepts_non_trusted_type_set_ns('a', 'b', null, 'null'); + }, "Element.setAttributeNS accepts null for non-specced accessor"); + + // Setup trusted values for use in subsequent tests. + const script_url = createScriptURL_policy(window, '5').createScriptURL(INPUTS.ScriptURL); + const html = createHTML_policy(window, '6').createHTML(INPUTS.HTML); + const script = createScript_policy(window, '7').createScript(INPUTS.Script); + + const xlink = "http://www.w3.org/1999/xlink"; + const svg = "http://www.w3.org/2000/svg"; + + // svg:script xlink:href=... expects a TrustedScriptURL. + // Assigning a TrustedScriptURL works. + test(t => { + let elem = document.createElementNS(svg, "script"); + elem.setAttributeNS(xlink, "href", script_url); + assert_equals("" + RESULTS.ScriptURL, + elem.getAttributeNodeNS(xlink, "href").value); + }, "Assigning TrustedScriptURL to <svg:script xlink:href=...> works"); + + // Assigning things that ought to not work. + test(t => { + let elem = document.createElementNS(svg, "script"); + const values = [ "abc", null, html, script ]; + for (const v of values) { + assert_throws_js(TypeError, _ => { + elem.setAttributeNS(xlink, "href", v); + }); + } + }, "Blocking non-TrustedScriptURL assignment to <svg:script xlink:href=...> works"); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html new file mode 100644 index 0000000000..34975f9d68 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container') + + // TrustedHTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + + var d = document.createElement('div'); + document.querySelector('#container').appendChild(d); + d.setHTMLUnsafe(html); + assert_equals(container.innerText, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "element.setHTMLUnsafe(html) assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + assert_throws_js(TypeError, _ => { + d.setHTMLUnsafe("Fail"); + }); + assert_equals(container.innerText, ""); + while (container.firstChild) + container.firstChild.remove(); + }, "`element.setHTMLUnsafe(string)` throws."); + + // Null assignment throws. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + assert_throws_js(TypeError, _ => { + d.outerHTML = null; + }); + assert_equals(container.innerText, ""); + while (container.firstChild) + container.firstChild.remove(); + }, "`element.setHTMLUnsafe(null)` throws."); + + // After default policy creation string assignment implicitly calls createHTML. + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + + var d = document.createElement('div'); + document.querySelector('#container').appendChild(d); + d.setHTMLUnsafe(INPUTS.HTML); + assert_equals(container.innerText, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "`element.setHTMLUnsafe(string)` assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + var d = document.createElement('div'); + container.appendChild(d); + d.setHTMLUnsafe(null); + assert_equals(container.innerText, "null"); + + while (container.firstChild) + container.firstChild.remove(); + }, "`element.setHTMLUnsafe(string)` assigned via default policy does not throw"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-HTMLElement-generic.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-HTMLElement-generic.html new file mode 100644 index 0000000000..9e780c1ed2 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-HTMLElement-generic.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<script> + var testnb = 0; + // TrustedScriptURL Assignments + const scriptURLTestCases = [ + [ 'embed', 'src' ], + [ 'object', 'codeBase' ], + [ 'object', 'data' ], + [ 'script', 'src' ] + ]; + + testnb = 0; + scriptURLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_script_url(window, ++testnb, t, c[0], c[1], RESULTS.SCRIPTURL); + assert_throws_no_trusted_type(c[0], c[1], 'A string'); + assert_throws_no_trusted_type(c[0], c[1], null); + }, c[0] + "." + c[1] + " accepts only TrustedScriptURL"); + }); + + // TrustedHTML Assignments + const HTMLTestCases = [ + [ 'div', 'innerHTML' ], + [ 'iframe', 'srcdoc' ] + ]; + + testnb = 0; + HTMLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_html(window, ++testnb, t, c[0], c[1], RESULTS.HTML); + assert_throws_no_trusted_type(c[0], c[1], 'A string'); + assert_throws_no_trusted_type(c[0], c[1], null); + }, c[0] + "." + c[1] + " accepts only TrustedHTML"); + }); + + // After default policy creation string and null assignments implicitly call createHTML + let p = window.trustedTypes.createPolicy("default", { createScriptURL: createScriptURLJS, createHTML: createHTMLJS }, true); + + scriptURLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_type(c[0], c[1], INPUTS.SCRIPTURL, RESULTS.SCRIPTURL); + assert_element_accepts_trusted_type(c[0], c[1], null, window.location.toString().replace(/[^\/]*$/, "null")); + }, c[0] + "." + c[1] + " accepts string and null after default policy was created"); + }); + + + HTMLTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_type(c[0], c[1], INPUTS.HTML, RESULTS.HTML); + assert_element_accepts_trusted_type(c[0], c[1], null, c[1] === 'innerHTML'? "" : "null"); + }, c[0] + "." + c[1] + " accepts string and null after default policy was created"); + }); + + // TrustedScript Assignments + const scriptTestCases = [ + [ 'script', 'text' ], + [ 'script', 'innerText' ], + [ 'script', 'textContent' ] + ]; + + testnb = 0; + scriptTestCases.forEach(c => { + test(t => { + assert_element_accepts_trusted_script(window, ++testnb, t, c[0], c[1], RESULTS.SCRIPT); + assert_throws_no_trusted_type(c[0], c[1], 'A string'); + assert_throws_no_trusted_type(c[0], c[1], null); + }, c[0] + "." + c[1] + " accepts only TrustedScript"); + }); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-Range-createContextualFragment.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Range-createContextualFragment.html new file mode 100644 index 0000000000..7911120493 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-Range-createContextualFragment.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +<body> +<script> + // TrustedHTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + var result = range.createContextualFragment(html); + assert_equals(result.textContent, RESULTS.HTML); + }, "range.createContextualFragment assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + assert_throws_js(TypeError, _ => { + var result = range.createContextualFragment("A string"); + }); + }, "`range.createContextualFragment(string)` throws."); + + // Null assignment throws. + test(t => { + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + assert_throws_js(TypeError, _ => { + var result = range.createContextualFragment(null); + }); + }, "`range.createContextualFragment(null)` throws."); + + // After default policy creation string assignment implicitly calls createHTML + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + var result = range.createContextualFragment(INPUTS.HTML); + assert_equals(result.textContent, RESULTS.HTML); + }, "`range.createContextualFragment(string)` assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML + test(t => { + var range = document.createRange(); + range.selectNodeContents(document.documentElement); + var result = range.createContextualFragment(null); + assert_equals(result.textContent, "null"); + }, "`range.createContextualFragment(null)` assigned via default policy does not throw."); +</script> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-ShadowRoot-setHTMLUnsafe.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-ShadowRoot-setHTMLUnsafe.html new file mode 100644 index 0000000000..a76543b3f6 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-ShadowRoot-setHTMLUnsafe.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> + + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<div id="container"></div> +<script> + var container = document.querySelector('#container') + + // TrustedHTML assignments do not throw. + test(t => { + let p = createHTML_policy(window, 1); + let html = p.createHTML(INPUTS.HTML); + + let d = document.createElement('div'); + let s = d.attachShadow({mode: 'open'}); + document.querySelector('#container').appendChild(d); + s.setHTMLUnsafe(html); + assert_equals(s.innerHTML, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "shadowRoot.setHTMLUnsafe(html) assigned via policy (successful HTML transformation)."); + + // String assignments throw. + test(t => { + let d = document.createElement('div'); + let s = d.attachShadow({mode: 'open'}); + container.appendChild(d); + assert_throws_js(TypeError, _ => { + s.setHTMLUnsafe("Fail"); + }); + assert_equals(s.innerHTML, ""); + while (container.firstChild) + container.firstChild.remove(); + }, "`shadowRoot.setHTMLUnsafe(string)` throws."); + + // Null assignment throws. + test(t => { + let d = document.createElement('div'); + let s = d.attachShadow({mode: 'open'}); + container.appendChild(d); + assert_throws_js(TypeError, _ => { + s.setHTMLUnsafe(null); + }); + assert_equals(s.innerHTML, ""); + while (container.firstChild) + container.firstChild.remove(); + }, "`shadowRoot.setHTMLUnsafe(null)` throws."); + + // After default policy creation string assignment implicitly calls createHTML. + test(t => { + let p = window.trustedTypes.createPolicy("default", { createHTML: createHTMLJS }, true); + + let d = document.createElement('div'); + let s = d.attachShadow({mode: 'open'}); + document.querySelector('#container').appendChild(d); + s.setHTMLUnsafe(INPUTS.HTML); + assert_equals(s.innerHTML, RESULTS.HTML); + + while (container.firstChild) + container.firstChild.remove(); + }, "`shadowRoot.setHTMLUnsafe(string)` assigned via default policy (successful HTML transformation)."); + + // After default policy creation null assignment implicitly calls createHTML. + test(t => { + let d = document.createElement('div'); + let s = d.attachShadow({mode: 'open'}); + container.appendChild(d); + s.setHTMLUnsafe(null); + assert_equals(s.innerHTML, "null"); + + while (container.firstChild) + container.firstChild.remove(); + }, "`shadowRoot.setHTMLUnsafe(string)` assigned via default policy does not throw"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html b/testing/web-platform/tests/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html new file mode 100644 index 0000000000..b881e8cb37 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> + <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> +<div id="testcases"> + <embed src="x"></embed> + <object data="x"></object> + <object codeBase="x"></object> + <script src="x"></script> + <iframe srcdoc="x"></iframe> + <div onclick="x"></div> +</div> +<div id="sanitycheck"> + <div style=sdf></div> + <p class=""></p> +</div> +<script> +test(t => { + const nodes = document.querySelectorAll("#sanitycheck *"); + nodes[0].attributes[0].textContent = "xxx"; + nodes[1].attributes[0].nodeValue = "yyy"; +}, "Sanity check: Setting non-TT attributes still works."); + +for (const element of document.querySelectorAll("#testcases *")) { + test(t => { + assert_throws_js(TypeError, _ => { + element.attributes[0].textContent = "sldkjsfldk"; + }); + }, `Set ${element.localName}.${element.attributes[0].localName} via textContent`); + test(t => { + assert_throws_js(TypeError, _ => { + element.attributes[0].nodeValue = "sdflkgjdlkgjdg"; + }); + }, `Set ${element.localName}.${element.attributes[0].localName} via nodeValue`); +}; +</script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/block-text-node-insertion-into-script-element.html b/testing/web-platform/tests/trusted-types/block-text-node-insertion-into-script-element.html new file mode 100644 index 0000000000..08e3e69553 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/block-text-node-insertion-into-script-element.html @@ -0,0 +1,238 @@ +<!DOCTYPE html> +<html> +<head> + <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> +<div id="container"></div> +<script id="script1">"hello world!";</script> +<script id="trigger"></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); + + // Regression tests for 'Bypass via insertAdjacentText', reported at + // https://github.com/w3c/trusted-types/issues/133 + + // We are trying to assert that scripts do _not_ get executed. We + // accomplish by having the script under examination containing a + // postMessage, and to send a second guaranteed-to-execute postMessage + // so there's a point in time when we're sure the first postMessage + // must have arrived (if indeed it had been sent). + // + // We'll interpret the message data as follows: + // - includes "block": error (this message should have been blocked by TT) + // - includes "count": Count these, and later check against expect_count. + // - includes "done": Unregister the event handler and finish the test. + // - all else: Reject, as this is probably an error in the test. + function checkMessage(expect_count) { + postMessage("done", "*"); + return new Promise((resolve, reject) => { + let count = 0; + globalThis.addEventListener("message", function handler(e) { + if (e.data.includes("block")) { + reject(`'block' received (${e.data})`); + } else if (e.data.includes("count")) { + count = count + 1; + } else if (e.data.includes("done")) { + globalThis.removeEventListener("message", handler); + if (expect_count && count != expect_count) { + reject( + `'done' received, but unexpected counts: expected ${expect_count} != actual ${count} (${e.data})`); + } else { + resolve(e.data); + } + } else { + reject("unexpected message received: " + e.data); + } + }); + }); + } + + function checkSecurityPolicyViolationEvent(expect_count) { + return new Promise((resolve, reject) => { + let count = 0; + document.addEventListener("securitypolicyviolation", e => { + if (e.sample.includes("trigger")) { + if (expect_count && count != expect_count) { + reject( + `'trigger' received, but unexpected counts: expected ${expect_count} != actual ${count}`); + } else { + resolve(); + } + } else { + count = count + 1; + } + }); + try { + document.getElementById("trigger").text = "trigger fail"; + } catch(e) { } + }); + } + + // Original report: + promise_test(t => { + // Setup: Create a <script> element with a <p> child. + let s = document.createElement("script"); + + // 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> + p.insertAdjacentText("beforebegin", + "postMessage('beforebegin should be blocked', '*');"); + assert_true(s.text.includes("postMessage")); + p.insertAdjacentText("afterend", + "postMessage('afterend should be blocked', '*');"); + assert_true(s.text.includes("after")); + return checkMessage(); + }, "Regression test: Bypass via insertAdjacentText, initial comment."); + + const script_elements = { + "script": doc => doc.createElement("script"), + "svg:script": doc => doc.createElementNS("http://www.w3.org/2000/svg", "script"), + }; + for (let [element, create_element] of Object.entries(script_elements)) { + // Like the "Original Report" test case above, but avoids use of the "text" + // accessor that <svg:script> doesn't support. + promise_test(t => { + let s = create_element(document); + + // 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> + p.insertAdjacentText("beforebegin", + "postMessage('beforebegin should be blocked', '*');"); + assert_true(s.textContent.includes("postMessage")); + p.insertAdjacentText("afterend", + "postMessage('afterend should be blocked', '*');"); + assert_true(s.textContent.includes("after")); + return checkMessage(); + }, "Regression test: Bypass via insertAdjacentText, initial comment. " + element); + + // Variant: Create a <script> element and create & insert a text node. Then + // insert it into the document container to make it live. + promise_test(t => { + // Setup: Create a <script> element, and insert text via a text node. + let s = create_element(document); + let text = document.createTextNode("postMessage('appendChild with a " + + "text node should be blocked', '*');"); + s.appendChild(text); + container.appendChild(s); + return checkMessage(); + }, "Regression test: Bypass via appendChild into off-document script element" + 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(t => { + // Setup: Create a <script> element, insert it into the doc, and then create + // and insert text via a text node. + let s = create_element(document); + container.appendChild(s); + let text = document.createTextNode("postMessage('appendChild with a live " + + "text node should be blocked', '*');"); + s.appendChild(text); + return checkMessage(); + }, "Regression test: Bypass via appendChild into live script element " + element); + } + + promise_test(t => { + return checkSecurityPolicyViolationEvent(); + }, "Prep for subsequent tests: Clear SPV event queue."); + + promise_test(t => { + const inserted_script = document.getElementById("script1"); + assert_throws_js(TypeError, _ => { + inserted_script.innerHTML = "2+2"; + }); + + let new_script = document.createElement("script"); + assert_throws_js(TypeError, _ => { + new_script.innerHTML = "2+2"; + container.appendChild(new_script); + }); + + const trusted_html = policy.createHTML("2*4"); + assert_equals("" + trusted_html, "3*4"); + inserted_script.innerHTML = trusted_html; + assert_equals(inserted_script.textContent, "3*4"); + + new_script = document.createElement("script"); + new_script.innerHTML = trusted_html; + container.appendChild(new_script); + assert_equals(inserted_script.textContent, "3*4"); + + // We expect 3 SPV events: two for the two assert_throws_js cases, and one + // for script element, which will be rejected at the time of execution. + return checkSecurityPolicyViolationEvent(3); + }, "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); + return Promise.resolve(); + }, "Prep for subsequent tests: Create default policy."); + + for (let [element, create_element] of Object.entries(script_elements)) { + promise_test(t => { + let s = create_element(document); + let text = document.createTextNode("postMessage('default', '*');"); + s.appendChild(text); + container.appendChild(s); + return Promise.all([checkMessage(1), + checkSecurityPolicyViolationEvent(0)]); + }, "Test that default policy applies. " + element); + + promise_test(t => { + let s = create_element(document); + let text = document.createTextNode("fail"); + s.appendChild(text); + container.appendChild(s); + return Promise.all([checkMessage(0), + checkSecurityPolicyViolationEvent(1)]); + }, "Test a failing default policy. " + element); + } + + promise_test(t => { + const inserted_script = document.getElementById("script1"); + inserted_script.innerHTML = "2+2"; + assert_equals(inserted_script.textContent, "3+3"); + + let new_script = document.createElement("script"); + new_script.innerHTML = "2+2"; + container.appendChild(new_script); + assert_equals(inserted_script.textContent, "3+3"); + + const trusted_html = default_policy.createHTML("2*4"); + assert_equals("" + trusted_html, "3*4"); + inserted_script.innerHTML = trusted_html; + assert_equals(inserted_script.textContent, "3*4"); + + new_script = document.createElement("script"); + new_script.innerHTML = trusted_html; + container.appendChild(new_script); + assert_equals(inserted_script.textContent, "3*4"); + + return checkSecurityPolicyViolationEvent(0); + }, "Spot tests around script + innerHTML interaction with default policy."); +</script> +</body> +</html> + diff --git a/testing/web-platform/tests/trusted-types/csp-block-eval.html b/testing/web-platform/tests/trusted-types/csp-block-eval.html new file mode 100644 index 0000000000..e3911bf9e6 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/csp-block-eval.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + + <!-- Note: Trusted Types enforcement, and a CSP that does not blanket-allow eval. --> + <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc'; require-trusted-types-for 'script'"> +</head> +<body> +<script nonce="abc"> + const p = createScript_policy(window, 1); + + test(t => { + let a = 0; + assert_throws_js(EvalError, _ => { + eval('a="hello there"'); + }); + assert_equals(a, 0); + }, "eval with plain string throws (both block)."); + + test(t => { + let a = 0; + assert_throws_js(EvalError, _ => { + eval(p.createScript('a="Hello transformed string"')); + }); + assert_equals(a, 0); + }, "eval with TrustedScript throws (script-src blocks)."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/default-policy-callback-arguments.html b/testing/web-platform/tests/trusted-types/default-policy-callback-arguments.html new file mode 100644 index 0000000000..a4a9c9e351 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/default-policy-callback-arguments.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<head> + <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> + <div id="log"></div> + <div id="div"></div> + <script id="script"></script> + <script> + var current_case = undefined; + function checker(...args) { + assert_equals(args.length, 3); + assert_true(current_case && current_case.length == 4); + assert_equals(args[0], current_case[0], "Expecting the value."); + assert_equals(args[1], current_case[1], "Expecting the type name."); + assert_equals(args[2], current_case[2], "Expecting the sink name."); + return args[0]; + } + + trustedTypes.createPolicy("default", { + createHTML: checker, + createScript: checker, + createScriptURL: checker + }); + + const div = document.getElementById("div"); + const script = document.getElementById("script"); + const cases = [ + [ "abc", "TrustedHTML", "Element innerHTML", + _ => div.innerHTML = "abc" ], + [ "2+2", "TrustedScript", "Node textContent", + _ => script.textContent = "2+2" ], + [ "about:blank", "TrustedScriptURL", "HTMLScriptElement src", + _ => script.src = "about:blank" ], + [ "2+2", "TrustedScript", "eval", _ => eval("2+2") ], + [ "(function anonymous(\n) {\nreturn 2+2\n})", "TrustedScript", + "Function", _ => new Function("return 2+2") ], + ]; + for (var tc of cases) { + test(t => { + current_case = tc; + tc[3](); + }, `Test callback arguments, case ${tc[2]}`); + } + </script> +</body> + diff --git a/testing/web-platform/tests/trusted-types/default-policy-report-only.html b/testing/web-platform/tests/trusted-types/default-policy-report-only.html new file mode 100644 index 0000000000..1cff751a80 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/default-policy-report-only.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> +</head> +<body> +<script> +// Ensure that only the right events trigger violation reports. +// The Promise will resolve, when an event including the string "done" is +// received. The last line of this test file will cause this trigger. +promise_test(t => { + let count = { "null": 0, "undefined": 0, "nodefault": 0 }; + return new Promise((resolve, reject) => { + document.addEventListener("securitypolicyviolation", e => { + e.stopPropagation(); + // We count the violation reports. We expect one each for "null" and + // "undefined", one each for the "no default" test case above, and one + // for the "done" line at the end, which signals the end of the test run. + if (e.sample.includes("done")) { + resolve(count); + } else if (e.sample.includes("null")) { + count["null"]++; + } else if (e.sample.includes("undefined")) { + count["undefined"]++; + } else if (e.sample.includes("nodefault")) { + count["nodefault"]++; + } else { + reject(); + } + }); + }).then(counters => { + for (const counter of ["null", "undefined", "nodefault"]) { + assert_equals(counters[counter], testCases.length, + "event count of " + counter); + } + }); +}, "Count SecurityPolicyViolation events."); + +const testCases = [ + [ "script", "src" ], + [ "div", "innerHTML" ], + [ "script", "text" ], +]; + +// Try each test case _without_ a default policy. +testCases.forEach(c => { + test(t => { + const element = document.createElement(c[0]); + element[c[1]] = "nodefault"; + assert_true(element[c[1]].includes("nodefault")); + }, `${c[0]}.${c[1]} no default policy`); +}); + +// A trusted type policy that forces a number of edge cases. +function policy(str) { + if (str == "throw") + throw RangeError(); + else if (str == "null") + return null; + else if (str == "undefined") + return undefined; + else if (str == "typeerror") + return document.bla(); + else if (str == "done") + return null; + else + return "sanitized: " + str; +} + +trustedTypes.createPolicy("default", { + createScriptURL: policy, + createHTML: policy, + createScript: policy +}); + +testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + const element = document.createElement(c[0]); + element[c[1]] = "abc"; + assert_equals(element[c[1]], "sanitized: abc"); + }, name + "default"); + test(t => { + const element = document.createElement(c[0]); + element[c[1]] = "null"; + assert_true(element[c[1]].includes("null")); + }, name + "null"); + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(RangeError, _ => element[c[1]] = "throw"); + }, name + "throw"); + test(t => { + const element = document.createElement(c[0]); + element[c[1]] = "undefined"; + assert_true(element[c[1]].includes("undefined")); + }, name + "undefined"); + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(TypeError, _ => element[c[1]] = "typeerror"); + }, name + "typeerror"); +}); + +// Trigger the exit condition in the "Count" promise test above. +try { document.createElement("script").text = "done"; } catch (e) {} +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/default-policy-report-only.html.headers b/testing/web-platform/tests/trusted-types/default-policy-report-only.html.headers new file mode 100644 index 0000000000..4c1ff15e16 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/default-policy-report-only.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/default-policy.html b/testing/web-platform/tests/trusted-types/default-policy.html new file mode 100644 index 0000000000..debde85cda --- /dev/null +++ b/testing/web-platform/tests/trusted-types/default-policy.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="support/helper.sub.js"></script> +</head> +<body> +<script> +// Ensure that only the right events trigger violation reports. +// The Promise will resolve, when an event including the string "done" is +// received. The last line of this test file will cause this trigger. +promise_test(t => { + let count = { "null": 0, "undefined": 0, "nodefault": 0 }; + return new Promise((resolve, reject) => { + document.addEventListener("securitypolicyviolation", e => { + e.stopPropagation(); + // We count the violation reports. We expect one each for "null" and + // "undefined", one each for the "no default" test case above, and one + // for the "done" line at the end, which signals the end of the test run. + if (e.sample.includes("done")) { + resolve(count); + } else if (e.sample.includes("null")) { + count["null"]++; + } else if (e.sample.includes("undefined")) { + count["undefined"]++; + } else if (e.sample.includes("nodefault")) { + count["nodefault"]++; + } else { + reject(); + } + }); + }).then(counters => { + for (const counter of ["null", "undefined", "nodefault"]) { + assert_equals(counters[counter], testCases.length, + "event count of " + counter); + } + }); +}, "Count SecurityPolicyViolation events."); + +const testCases = [ + [ "script", "src" ], + [ "div", "innerHTML" ], + [ "script", "text" ], +]; + +// Try each test case _without_ a default policy. +testCases.forEach(c => { + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(TypeError, _ => element[c[1]] = "nodefault"); + assert_equals(element[c[1]], ""); + }, `${c[0]}.${c[1]} no default policy`); +}); + +// A trusted type policy that forces a number of edge cases. +function policy(str) { + if (str == "throw") + throw RangeError(); + else if (str == "null") + return null; + else if (str == "undefined") + return undefined; + else if (str == "typeerror") + return document.bla(); + else if (str == "done") + return null; + else + return "sanitized: " + str; +} + +trustedTypes.createPolicy("default", { + createScriptURL: policy, + createHTML: policy, + createScript: policy +}); + +testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + const element = document.createElement(c[0]); + element[c[1]] = "abc"; + assert_equals(element[c[1]], "sanitized: abc"); + }, name + "default"); + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(TypeError, _ => element[c[1]] = "null"); + assert_equals(element[c[1]], ""); + }, name + "null"); + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(RangeError, _ => element[c[1]] = "throw"); + }, name + "throw"); + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(TypeError, _ => element[c[1]] = "undefined"); + assert_equals(element[c[1]], ""); + }, name + "undefined"); + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(TypeError, _ => element[c[1]] = "typeerror"); + }, name + "typeerror"); +}); + +// Trigger the exit condition in the "Count" promise test above. +try { document.createElement("script").text = "done"; } catch (e) {} +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/default-policy.html.headers b/testing/web-platform/tests/trusted-types/default-policy.html.headers new file mode 100644 index 0000000000..604e765da4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/default-policy.html.headers @@ -0,0 +1 @@ +Content-Security-Policy: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/empty-default-policy-report-only.html b/testing/web-platform/tests/trusted-types/empty-default-policy-report-only.html new file mode 100644 index 0000000000..1ba9c5ec18 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/empty-default-policy-report-only.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +// Ensure that only the right events trigger violation reports. +// The Promise will resolve, when an event including the string "done" is +// received. The last line of this test file will cause this trigger. +promise_test(t => { + let count = 0; + return new Promise((resolve, reject) => { + document.addEventListener("securitypolicyviolation", e => { + e.stopPropagation(); + // We count the violation reports. We expect one each for "abc" test cases, and one + // for the "done" line at the end, which signals the end of the test run. + if (e.sample.includes("done")) { + resolve(count); + } else if (e.sample.includes("abc")) { + count++; + } else { + reject(); + } + }); + }).then(counter => { + assert_equals(counter, testCases.length, "event count"); + }); +}, "Count SecurityPolicyViolation events."); + +const testCases = [ + [ "script", "src" ], + [ "div", "innerHTML" ], + [ "script", "text" ], +]; + +trustedTypes.createPolicy("default", {}); + +testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + const element = document.createElement(c[0]); + element[c[1]] = "abc"; + if (c[1] == "src") { + assert_equals(element[c[1]], location.protocol + "//" + location.host + "/trusted-types/abc"); + } else { + assert_equals(element[c[1]], "abc"); + } + }, name + "default"); +}); + +// Trigger the exit condition in the "Count" promise test above. +try { document.createElement("script").text = "done"; } catch (e) {} +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/empty-default-policy-report-only.html.headers b/testing/web-platform/tests/trusted-types/empty-default-policy-report-only.html.headers new file mode 100644 index 0000000000..4c1ff15e16 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/empty-default-policy-report-only.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/empty-default-policy.html b/testing/web-platform/tests/trusted-types/empty-default-policy.html new file mode 100644 index 0000000000..d31b48ecd5 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/empty-default-policy.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +// Ensure that only the right events trigger violation reports. +// The Promise will resolve, when an event including the string "done" is +// received. The last line of this test file will cause this trigger. +promise_test(t => { + let count = 0; + return new Promise((resolve, reject) => { + document.addEventListener("securitypolicyviolation", e => { + e.stopPropagation(); + // We count the violation reports. We expect one each for "abc" test cases, and one + // for the "done" line at the end, which signals the end of the test run. + if (e.sample.includes("done")) { + resolve(count); + } else if (e.sample.includes("abc")) { + count++; + } else { + reject(); + } + }); + }).then(counter => { + assert_equals(counter, testCases.length, "event count"); + }); +}, "Count SecurityPolicyViolation events."); + +const testCases = [ + [ "script", "src" ], + [ "div", "innerHTML" ], + [ "script", "text" ], +]; + +trustedTypes.createPolicy("default", {}); + +testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + const element = document.createElement(c[0]); + assert_throws_js(TypeError, _ => element[c[1]] = "abc"); + assert_equals(element[c[1]], ""); + }, name + "default"); +}); + +// Trigger the exit condition in the "Count" promise test above. +try { document.createElement("script").text = "done"; } catch (e) {} +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/empty-default-policy.html.headers b/testing/web-platform/tests/trusted-types/empty-default-policy.html.headers new file mode 100644 index 0000000000..604e765da4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/empty-default-policy.html.headers @@ -0,0 +1 @@ +Content-Security-Policy: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/eval-csp-no-tt.html b/testing/web-platform/tests/trusted-types/eval-csp-no-tt.html new file mode 100644 index 0000000000..6720d80fe7 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-csp-no-tt.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + <meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-eval' 'nonce-abc'"> +</head> +<body> +<script nonce="abc"> + const p = trustedTypes.createPolicy("p", {createScript: s => s}); + + test(t => { + assert_equals(eval(p.createScript("1+1")), 2); + }, "eval of TrustedScript works."); + + test(t => { + assert_equals(eval('1+1'), 2); + }, "eval of string works."); + + test(t => { + assert_equals(eval(42), 42); + assert_object_equals(eval({}), {}); + assert_equals(eval(null), null); + assert_equals(eval(undefined), undefined); + }, "eval of !TrustedScript and !string works."); + + test(t => { + assert_equals(new Function(p.createScript("return 1+1"))(), 2); + }, "Function constructor of TrustedScript works."); + + test(t => { + assert_equals(new Function('return 1+1')(), 2); + }, "Function constructor of string works."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/eval-csp-tt-default-policy.html b/testing/web-platform/tests/trusted-types/eval-csp-tt-default-policy.html new file mode 100644 index 0000000000..9afe571199 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-csp-tt-default-policy.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'"> +</head> +<body> +<script> + trustedTypes.createPolicy("default", {createScript: s => s.replace("1", "4")}); + const p = trustedTypes.createPolicy("p", {createScript: s => s}); + + test(t => { + assert_equals(eval(p.createScript('1+1')), 2); + }, "eval of TrustedScript works."); + + test(t => { + assert_equals(eval('1+1'), 5); // '1+1' becomes '4+1'. + }, "eval of string works."); + + test(t => { + assert_equals(eval(42), 42); + assert_object_equals(eval({}), {}); + assert_equals(eval(null), null); + assert_equals(eval(undefined), undefined); + }, "eval of !TrustedScript and !string works."); + + test(t => { + assert_equals(new Function(p.createScript('return 1+1'))(), 2); + }, "Function constructor of TrustedScript works."); + + test(t => { + assert_equals(new Function('return 1+1')(), 5); + }, "Function constructor of string works."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/eval-csp-tt-no-default-policy.html b/testing/web-platform/tests/trusted-types/eval-csp-tt-no-default-policy.html new file mode 100644 index 0000000000..8c4aba11ec --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-csp-tt-no-default-policy.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> +</head> +<body> +<script> + const p = trustedTypes.createPolicy("p", {createScript: s => s}); + + test(t => { + assert_equals(eval(p.createScript('1+1')), 2); + }, "eval of TrustedScript works."); + + test(t => { + assert_throws_js(EvalError, _ => eval('1+1')); + }, "eval of string fails."); + + test(t => { + assert_equals(eval(42), 42); + assert_object_equals(eval({}), {}); + assert_equals(eval(null), null); + assert_equals(eval(undefined), undefined); + }, "eval of !TrustedScript and !string works."); + + test(t => { + assert_equals(new Function(p.createScript('return 1+1'))(), 2); + }, "Function constructor of TrustedScript works."); + + test(t => { + assert_throws_js(EvalError, _ => new Function('return 1+1')()); + }, "Function constructor of string fails."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/eval-function-constructor.html b/testing/web-platform/tests/trusted-types/eval-function-constructor.html new file mode 100644 index 0000000000..a20bc4a78d --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-function-constructor.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" 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 }); + const args = ["a", "b", "c = 5", "return (a+b)*c;"]; + const arg_max = 2 ** args.length -1; + + // Call 'new Function(...args)', but with a subet of args being Strings, + // and a subset being TrustedScript. We use a bitmask to determine which + // argument gets to be trusted or not. + function new_function_with_maybe_trusted_args(mask) { + let maybe_trusted_args = args.map((value, arg_nr) => { + return (mask & (2**arg_nr)) ? policy.createScript(value) : value; + }); + return new Function(...maybe_trusted_args); + } + + // Generate all combinations of String/TrustedScript, except for the one + // where all argumentes are TrustedScript. + for (let mask = 0; mask < arg_max; mask++) { + test(t => { + assert_throws_js(EvalError, + _ => new_function_with_maybe_trusted_args(mask)); + }, "Function constructor with mixed plain and trusted strings, mask #" + mask); + } + + // Now do one with all trusted arguments. + test(t => { + const f = new_function_with_maybe_trusted_args(arg_max); + assert_equals(f(1,2,3), 9); + assert_equals(f(1,2), 15); + }, "Function constructor with mixed plain and trusted strings, mask #" + arg_max); +</script> + diff --git a/testing/web-platform/tests/trusted-types/eval-no-csp-no-tt-default-policy.html b/testing/web-platform/tests/trusted-types/eval-no-csp-no-tt-default-policy.html new file mode 100644 index 0000000000..d36afbc8ac --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-no-csp-no-tt-default-policy.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + <!-- No CSP header. --> +</head> +<body> +<script> + trustedTypes.createPolicy("default", {createScript: s => s + 4}); + const p = trustedTypes.createPolicy("p", {createScript: s => s}); + + test(t => { + assert_equals(eval(p.createScript('1+1')), 2); + }, "eval of TrustedScript works."); + + test(t => { + assert_equals(eval('1+1'), 2); + }, "eval of string works and does not call a default policy."); + + test(t => { + assert_equals(eval(42), 42); + assert_object_equals(eval({}), {}); + assert_equals(eval(null), null); + assert_equals(eval(undefined), undefined); + }, "eval of !TrustedScript and !string works."); + + test(t => { + assert_equals(new Function(p.createScript('return 1+1'))(), 2); + }, "Function constructor of TrustedScript works."); + + test(t => { + assert_equals(new Function('return 1+1')(), 2); + }, "Function constructor of string works and does not call a default policy."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/eval-no-csp-no-tt.html b/testing/web-platform/tests/trusted-types/eval-no-csp-no-tt.html new file mode 100644 index 0000000000..3013c08447 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-no-csp-no-tt.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + <!-- No CSP header. --> +</head> +<body> +<script nonce="abc"> + const p = trustedTypes.createPolicy("p", {createScript: s => s}); + + test(t => { + assert_equals(eval(p.createScript('1+1')), 2); + }, "eval of TrustedScript works."); + + test(t => { + assert_equals(eval('1+1'), 2); + }, "eval of string works."); + + test(t => { + assert_equals(eval(42), 42); + assert_object_equals(eval({}), {}); + assert_equals(eval(null), null); + assert_equals(eval(undefined), undefined); + }, "eval of !TrustedScript and !string works."); + + test(t => { + assert_equals(new Function(p.createScript('return 1+1'))(), 2); + }, "Function constructor of TrustedScript works."); + + test(t => { + assert_equals(new Function('return 1+1')(), 2); + }, "Function constructor of string works."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/eval-with-permissive-csp.html b/testing/web-platform/tests/trusted-types/eval-with-permissive-csp.html new file mode 100644 index 0000000000..8910d4e7ef --- /dev/null +++ b/testing/web-platform/tests/trusted-types/eval-with-permissive-csp.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> + <script nonce="abc" src="/resources/testharness.js"></script> + <script nonce="abc" src="/resources/testharnessreport.js"></script> + <script nonce="abc" src="support/helper.sub.js"></script> + + <!-- Note: Trusted Types enforcement, and a CSP that allows all eval. --> + <meta http-equiv="Content-Security-Policy" + content="script-src 'nonce-abc' 'unsafe-eval'; require-trusted-types-for 'script'"> +</head> +<body> +<script nonce="abc"> + let p = createScript_policy(window, 1); + test(t => { + let a = 0; + assert_throws_js(EvalError, _ => { + eval('a="hello there"'); + }); + assert_equals(a, 0); + }, "eval with plain string with Trusted Types and permissive CSP throws (no type)."); + + test(t => { + let a = 0; + assert_throws_js(EvalError, _ => { + new Function('a="hello there"'); + }); + assert_equals(a, 0); + }, "Function constructor with plain string with Trusted Types and permissive CSP throws (no type)."); + + test(t => { + let s = eval(p.createScript('"Hello transformed string"')); + assert_equals("" + s, "Hello a cat string"); + }, "eval with TrustedScript and permissive CSP works."); + + test(t => { + let s = new Function(p.createScript('return "Hello transformed string"'))(); + assert_equals(s, "Hello a cat string"); + }, "new Function with TrustedScript and permissive CSP works."); + + trustedTypes.createPolicy("default", { createScript: createScriptJS }, true); + test(t => { + let s = eval('"Hello transformed untrusted string"'); + assert_equals(s, "Hello a cat untrusted string"); + }, "eval with default policy and permissive CSP still obeys default policy."); + + test(t => { + let s = new Function('return "Hello transformed untrusted string"')(); + assert_equals(s, "Hello a cat untrusted string"); + }, "new Function with default policy and permissive CSP still obeys default policy."); +</script> + diff --git a/testing/web-platform/tests/trusted-types/idlharness.tentative.window.js.html b/testing/web-platform/tests/trusted-types/idlharness.tentative.window.js.html new file mode 100644 index 0000000000..07847fdb39 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/idlharness.tentative.window.js.html @@ -0,0 +1,16 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js + +idl_test( + ['trusted-types'], + ['html', 'dom'], + idl_array => { + idl_array.add_objects({ + TrustedTypePolicyFactory: ['window.trustedTypes'], + TrustedTypePolicy: ['window.trustedTypes.createPolicy("SomeName", { createHTML: s => s })'], + TrustedHTML: ['window.trustedTypes.createPolicy("SomeName1", { createHTML: s => s }).createHTML("A string")'], + TrustedScript: ['window.trustedTypes.createPolicy("SomeName2", { createScript: s => s }).createScript("A string")'], + TrustedScriptURL: ['window.trustedTypes.createPolicy("SomeName3", { createScriptURL: s => s }).createScriptURL("A string")'], + }); + } +); diff --git a/testing/web-platform/tests/trusted-types/no-require-trusted-types-for-report-only.html b/testing/web-platform/tests/trusted-types/no-require-trusted-types-for-report-only.html new file mode 100644 index 0000000000..651bf0a719 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/no-require-trusted-types-for-report-only.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + const testCases = [ + ["script", "src"], + ["div", "innerHTML"], + ["script", "text"], + ]; + + testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + s = document.createElement(c[0]); + s[c[1]] = "https://example.com/"; + assert_equals("https://example.com/", s[c[1]].toString()); + }, name + "without trusted types"); + }); + + p = trustedTypes.createPolicy("policyA", + {createScript: s => s + 1, createHTML: s => s + 1, createScriptURL: s => s + 1}); + testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + s = document.createElement("script"); + script = p.createScript("1"); + s.innerText = script; + assert_equals(script.toString(), s.innerText.toString()); + }, name + "with trusted types"); + }); + + trustedTypes.createPolicy("default", {}); + testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + s = document.createElement("script"); + s.innerText = "1"; + assert_equals(s.innerText.toString(), "1"); + }, name + "empty default"); + }); +</script> diff --git a/testing/web-platform/tests/trusted-types/no-require-trusted-types-for-report-only.html.headers b/testing/web-platform/tests/trusted-types/no-require-trusted-types-for-report-only.html.headers new file mode 100644 index 0000000000..aa00fcc15a --- /dev/null +++ b/testing/web-platform/tests/trusted-types/no-require-trusted-types-for-report-only.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: require-trusted-types-for 'script' diff --git a/testing/web-platform/tests/trusted-types/no-require-trusted-types-for.html b/testing/web-platform/tests/trusted-types/no-require-trusted-types-for.html new file mode 100644 index 0000000000..651bf0a719 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/no-require-trusted-types-for.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + const testCases = [ + ["script", "src"], + ["div", "innerHTML"], + ["script", "text"], + ]; + + testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + s = document.createElement(c[0]); + s[c[1]] = "https://example.com/"; + assert_equals("https://example.com/", s[c[1]].toString()); + }, name + "without trusted types"); + }); + + p = trustedTypes.createPolicy("policyA", + {createScript: s => s + 1, createHTML: s => s + 1, createScriptURL: s => s + 1}); + testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + s = document.createElement("script"); + script = p.createScript("1"); + s.innerText = script; + assert_equals(script.toString(), s.innerText.toString()); + }, name + "with trusted types"); + }); + + trustedTypes.createPolicy("default", {}); + testCases.forEach(c => { + const name = `${c[0]}.${c[1]} `; + test(t => { + s = document.createElement("script"); + s.innerText = "1"; + assert_equals(s.innerText.toString(), "1"); + }, name + "empty default"); + }); +</script> diff --git a/testing/web-platform/tests/trusted-types/require-trusted-types-for-report-only.html b/testing/web-platform/tests/trusted-types/require-trusted-types-for-report-only.html new file mode 100644 index 0000000000..25b4440ef4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/require-trusted-types-for-report-only.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + + function promise_violation(filter_arg) { + return _ => new Promise((resolve, reject) => { + function handler(e) { + let matches = (filter_arg instanceof Function) + ? filter_arg(e) + : (e.originalPolicy.includes(filter_arg)); + if (matches) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + + document.addEventListener("securitypolicyviolation", handler); + }); + } + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")); + + d = document.createElement("div"); + d.innerHTML = "a"; + assert_equals("a", d.innerHTML); + return p; + }, "Require trusted types for 'script' block create HTML."); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")); + + d = document.createElement("script"); + d.innerText = "a"; + assert_equals("a", d.innerText); + return p; + }, "Require trusted types for 'script' block create script."); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")); + + s = document.createElement("script"); + s.src = "a"; + assert_true(s.src.includes("/trusted-types/a")); + return p; + }, "Require trusted types for 'script' block create script URL."); + + promise_test(t => { + return new Promise(resolve => { + p = trustedTypes.createPolicy("policyA", {createScript: s => s+1}); + p1 = trustedTypes.createPolicy("policyA", {createHTML: _ => ""}); + p2 = trustedTypes.createPolicy("default", {}); + script = p.createScript("1"); + assert_equals(script.toString(), "11"); + s = document.createElement("script"); + s.innerText = script; + assert_equals(script.toString(), s.innerText.toString()); + s.innerText = "1"; + assert_equals("1", s.innerText); + resolve(); + }); + }, "Set require trusted types for 'script' without CSP for trusted types don't block policy creation and using."); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/trusted-types/require-trusted-types-for-report-only.html.headers b/testing/web-platform/tests/trusted-types/require-trusted-types-for-report-only.html.headers new file mode 100644 index 0000000000..c6412f8d47 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/require-trusted-types-for-report-only.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: require-trusted-types-for 'script'
\ No newline at end of file diff --git a/testing/web-platform/tests/trusted-types/require-trusted-types-for.html b/testing/web-platform/tests/trusted-types/require-trusted-types-for.html new file mode 100644 index 0000000000..2a3820a89b --- /dev/null +++ b/testing/web-platform/tests/trusted-types/require-trusted-types-for.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<head> + <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> + + function promise_violation(filter_arg) { + return _ => new Promise((resolve, reject) => { + function handler(e) { + let matches = (filter_arg instanceof Function) + ? filter_arg(e) + : (e.originalPolicy.includes(filter_arg)); + if (matches) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + + document.addEventListener("securitypolicyviolation", handler); + }); + } + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")); + d = document.createElement("div"); + assert_throws_js(TypeError, + _ => { + d.innerHTML = "a"; + }); + assert_equals("", d.innerHTML); + return p; + }, "Require trusted types for 'script' block create HTML."); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")); + d = document.createElement("script"); + assert_throws_js(TypeError, + _ => { + d.innerText = "a"; + }); + assert_equals("", d.innerText); + return p; + }, "Require trusted types for 'script' block create script."); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")); + s = document.createElement("script"); + assert_throws_js(TypeError, + _ => { + s.src = "a"; + }); + assert_equals("", s.src); + return p; + }, "Require trusted types for 'script' block create script URL."); + + promise_test(t => { + return new Promise(resolve => { + p = trustedTypes.createPolicy("policyA", {createScript: s => s + 1}); + p1 = trustedTypes.createPolicy("policyA", {createHTML: _ => ""}); + p2 = trustedTypes.createPolicy("default", {createScript: s => s}); + script = p.createScript("1"); + assert_equals(script.toString(), "11"); + s = document.createElement("script"); + s.innerText = script; + assert_equals(script.toString(), s.innerText.toString()); + s.innerText = "1"; + assert_equals("1", s.innerText.toString()); + resolve(); + }); + }, "Set require trusted types for 'script' without CSP for trusted types don't block policy creation and using."); +</script> diff --git a/testing/web-platform/tests/trusted-types/support/HTMLScriptElement-internal-slot-support.html b/testing/web-platform/tests/trusted-types/support/HTMLScriptElement-internal-slot-support.html new file mode 100644 index 0000000000..b9a0ceefae --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/HTMLScriptElement-internal-slot-support.html @@ -0,0 +1,18 @@ +<html> +<head> +<meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script'"> +<script> +// Support script. This is meant to trigger deferred parsing. To accomplish this +// we pad the file (with this very comment) to >>200B length, and then load it +// with ...?pipe=tricle(200:d1) to ensure it's being delivered in pieces. +window.parent.postMessage("first script element executed", "*"); +</script> +<script> +window.parent.postMessage("second script element executed", "*"); +</script> +</head> +<body> +Hey! +</body> +</html> + diff --git a/testing/web-platform/tests/trusted-types/support/HTMLScriptElement-internal-slot-support.js b/testing/web-platform/tests/trusted-types/support/HTMLScriptElement-internal-slot-support.js new file mode 100644 index 0000000000..7d574a6a13 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/HTMLScriptElement-internal-slot-support.js @@ -0,0 +1,2 @@ +window.postMessage("script url", "*"); + diff --git a/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-eval.https.js b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-eval.https.js new file mode 100644 index 0000000000..be0a4300e1 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-eval.https.js @@ -0,0 +1,37 @@ +let test_setup_policy = trustedTypes.createPolicy("hurrayanythinggoes", { + createScriptURL: x => x +}); +importScripts(test_setup_policy.createScriptURL("/resources/testharness.js")); + +// Determine worker type (for better logging) +let worker_type = "unknown"; +if (this.DedicatedWorkerGlobalScope !== undefined) { + worker_type = "dedicated worker"; +} else if (this.SharedWorkerGlobalScope !== undefined) { + worker_type = "shared worker"; +} else if (this.ServiceWorkerGlobalScope !== undefined) { + worker_type = "service worker"; +} + +// Test eval(string) +test(t => { + assert_throws_js(EvalError, _ => eval("2")); +}, "eval(string) in " + worker_type); + +// Test eval(TrustedScript) +let test_policy = trustedTypes.createPolicy("xxx", { + createScript: x => x.replace("2", "7") +}); +test(t => { + assert_equals(eval(test_policy.createScript("2")), 7); +}, "eval(TrustedScript) in " + worker_type); + +// Test eval(String) with default policy +trustedTypes.createPolicy("default", { + createScript: x => x.replace("2", "5") +}); +test(t => { + assert_equals(eval("2"), 5); +}, "eval(string) with default policy in " + worker_type); + +done(); diff --git a/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-eval.https.js.headers b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-eval.https.js.headers new file mode 100644 index 0000000000..604e765da4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-eval.https.js.headers @@ -0,0 +1 @@ +Content-Security-Policy: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-importScripts.https.js b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-importScripts.https.js new file mode 100644 index 0000000000..c40e8550dd --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-importScripts.https.js @@ -0,0 +1,84 @@ +let test_setup_policy = trustedTypes.createPolicy("hurrayanythinggoes", { + createScriptURL: x => x +}); +importScripts(test_setup_policy.createScriptURL("/resources/testharness.js")); + +// Determine worker type (for better logging) +let worker_type = "unknown"; +if (this.DedicatedWorkerGlobalScope !== undefined) { + worker_type = "dedicated worker"; +} else if (this.SharedWorkerGlobalScope !== undefined) { + worker_type = "shared worker"; +} else if (this.ServiceWorkerGlobalScope !== undefined) { + worker_type = "service worker"; +} + +let test_policy = trustedTypes.createPolicy("xxx", { + createScriptURL: url => url.replace("play", "work") +}); + +test(t => { + self.result = "Fail"; + let trusted_url = test_policy.createScriptURL("player.js"); + assert_true(this.trustedTypes.isScriptURL(trusted_url)); + importScripts(trusted_url); // worker.js modifies self.result. + assert_equals(self.result, "Pass"); +}, "importScripts with TrustedScriptURL works in " + worker_type); + +test(t => { + let untrusted_url = "player.js"; + assert_throws_js(TypeError, + function() { importScripts(untrusted_url) }, + "importScripts(untrusted_url)"); +}, "importScripts with untrusted URLs throws in " + worker_type); + +test(t => { + assert_throws_js(TypeError, + function() { importScripts(null) }, + "importScripts(null)"); +}, "null is not a trusted script URL throws in " + worker_type); + +test(t => { + self.result = "Fail"; + let trusted_url = test_policy.createScriptURL("player.js?variant1"); + let trusted_url2 = test_policy.createScriptURL("player.js?variant2"); + importScripts(trusted_url, trusted_url2); + assert_equals(self.result, "Pass"); +}, "importScripts with two URLs, both trusted, in " + worker_type); + +test(t => { + let untrusted_url = "player.js?variant1"; + let untrusted_url2 = "player.js?variant2"; + assert_throws_js(TypeError, + function() { importScripts(untrusted_url, untrusted_url2) }, + "importScripts(untrusted_url, untrusted_url2)"); +}, "importScripts with two URLs, both strings, in " + worker_type); + +test(t => { + let untrusted_url = "player.js"; + let trusted_url = test_policy.createScriptURL(untrusted_url); + assert_throws_js(TypeError, + function() { importScripts(untrusted_url, trusted_url) }, + "importScripts(untrusted_url, trusted_url)"); +}, "importScripts with two URLs, one trusted, in " + worker_type); + +// Test default policy application: +trustedTypes.createPolicy("default", { + createScriptURL: url => url.replace("play", "work") +}, true); +test(t => { + self.result = "Fail"; + let untrusted_url = "player.js"; + importScripts(untrusted_url); + assert_equals(self.result, "Pass"); +}, "importScripts with untrusted URLs and default policy works in " + worker_type); + +test(t => { + self.result = "Fail"; + let untrusted_url = "player.js"; + let trusted_url = test_policy.createScriptURL(untrusted_url); + importScripts(untrusted_url, trusted_url); + assert_equals(self.result, "Pass"); +}, "importScripts with one trusted and one untrusted URLs and default policy works in " + worker_type); + +done(); diff --git a/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-importScripts.https.js.headers b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-importScripts.https.js.headers new file mode 100644 index 0000000000..604e765da4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/WorkerGlobalScope-importScripts.https.js.headers @@ -0,0 +1 @@ +Content-Security-Policy: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/support/frame-without-trusted-types.html b/testing/web-platform/tests/trusted-types/support/frame-without-trusted-types.html new file mode 100644 index 0000000000..25cf073e79 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/frame-without-trusted-types.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<head> +</head> +<body> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/support/helper.sub.js b/testing/web-platform/tests/trusted-types/support/helper.sub.js new file mode 100644 index 0000000000..2d1bd436bd --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/helper.sub.js @@ -0,0 +1,166 @@ +const INPUTS = { + HTML: "Hi, I want to be transformed!", + SCRIPT: "Hi, I want to be transformed!", + SCRIPTURL: "http://this.is.a.scripturl.test/", +}; + +const RESULTS = { + HTML: "Quack, I want to be a duck!", + SCRIPT: "Meow, I want to be a cat!", + SCRIPTURL: "http://this.is.a.successful.test/", +}; + +function createHTMLJS(html) { + return html.replace("Hi", "Quack") + .replace("transformed", "a duck"); +} + +function createScriptJS(script) { + return script.replace("Hi", "Meow") + .replace("transformed", "a cat"); +} + +function createScriptURLJS(scripturl) { + return scripturl.replace("scripturl", "successful"); +} + +function createHTMLJSWithThreeArguments(html0, html1, html2) { + return html0 + html1 + html2; +} + +function createScriptJSWithThreeArguments(script0, script1, script2) { + return script0 + script1 + script2; +} + +function createScriptURLJSWithThreeArguments(scripturl0, scripturl1, scripturl2) { + return scripturl0 + scripturl1 + scripturl2; +} + +function createHTML_policy(win, c) { + return win.trustedTypes.createPolicy('SomeHTMLPolicyName' + c, { createHTML: createHTMLJS }); +} + +function createScript_policy(win, c) { + return win.trustedTypes.createPolicy('SomeScriptPolicyName' + c, { createScript: createScriptJS }); +} + +function createScriptURL_policy(win, c) { + return win.trustedTypes.createPolicy('SomeScriptURLPolicyName' + c, { createScriptURL: createScriptURLJS }); +} + +function assert_element_accepts_trusted_html(win, c, t, tag, attribute, expected) { + let p = createHTML_policy(win, c); + let html = p.createHTML(INPUTS.HTML); + assert_element_accepts_trusted_type(tag, attribute, html, expected); +} + +function assert_element_accepts_trusted_script(win, c, t, tag, attribute, expected) { + let p = createScript_policy(win, c); + let script = p.createScript(INPUTS.SCRIPT); + assert_element_accepts_trusted_type(tag, attribute, script, expected); +} + +function assert_element_accepts_trusted_script_url(win, c, t, tag, attribute, expected) { + let p = createScriptURL_policy(win, c); + let scripturl = p.createScriptURL(INPUTS.SCRIPTURL); + assert_element_accepts_trusted_type(tag, attribute, scripturl, expected); +} + +function assert_element_accepts_trusted_type(tag, attribute, value, expected) { + let elem = document.createElement(tag); + elem[attribute] = value; + assert_equals(elem[attribute] + "", expected); +} + +function assert_throws_no_trusted_type(tag, attribute, value) { + let elem = document.createElement(tag); + let prev = elem[attribute]; + assert_throws_js(TypeError, _ => { + elem[attribute] = value; + }); + assert_equals(elem[attribute], prev); +} + +function assert_element_accepts_trusted_html_explicit_set(win, c, t, tag, attribute, expected) { + let p = createHTML_policy(win, c); + let html = p.createHTML(INPUTS.HTML); + assert_element_accepts_trusted_type_explicit_set(tag, attribute, html, expected); +} + +function assert_element_accepts_trusted_script_explicit_set(win, c, t, tag, attribute, expected) { + let p = createScript_policy(win, c); + let script = p.createScript(INPUTS.SCRIPT); + assert_element_accepts_trusted_type_explicit_set(tag, attribute, script, expected); +} + +function assert_element_accepts_trusted_script_url_explicit_set(win, c, t, tag, attribute, expected) { + let p = createScriptURL_policy(win, c); + let scripturl = p.createScriptURL(INPUTS.SCRIPTURL); + assert_element_accepts_trusted_type_explicit_set(tag, attribute, scripturl, expected); +} + +function assert_element_accepts_trusted_type_explicit_set(tag, attribute, value, expected) { + let elem = document.createElement(tag); + elem.setAttribute(attribute, value); + if (!/^on/.test(attribute)) { // "on" attributes are converted to functions. + assert_equals(elem[attribute] + "", expected); + } + assert_equals(elem.getAttribute(attribute), expected); +} + +function assert_throws_no_trusted_type_explicit_set(tag, attribute, value) { + let elem = document.createElement(tag); + let prev = elem[attribute]; + assert_throws_js(TypeError, _ => { + elem.setAttribute(attribute, value); + }); + assert_equals(elem[attribute], prev); + assert_equals(elem.getAttribute(attribute), null); +} + +function assert_element_accepts_non_trusted_type_explicit_set(tag, attribute, value, expected) { + let elem = document.createElement(tag); + elem.setAttribute(attribute, value); + assert_equals(elem[attribute] + "", expected); + assert_equals(elem.getAttribute(attribute), expected); +} + +let namespace = 'http://www.w3.org/1999/xhtml'; +function assert_element_accepts_trusted_html_set_ns(win, c, t, tag, attribute, expected) { + let p = createHTML_policy(win, c); + let html = p.createHTML(INPUTS.HTML); + assert_element_accepts_trusted_type_set_ns(tag, attribute, html, expected); +} + +function assert_element_accepts_trusted_script_set_ns(win, c, t, tag, attribute, expected) { + let p = createScript_policy(win, c); + let script = p.createScript(INPUTS.SCRIPT); + assert_element_accepts_trusted_type_set_ns(tag, attribute, script, expected); +} + +function assert_element_accepts_trusted_script_url_set_ns(win, c, t, tag, attribute, expected) { + let p = createScriptURL_policy(win, c); + let scripturl = p.createScriptURL(INPUTS.SCRIPTURL); + assert_element_accepts_trusted_type_set_ns(tag, attribute, scripturl, expected); +} + +function assert_element_accepts_trusted_type_set_ns(tag, attribute, value, expected) { + let elem = document.createElement(tag); + elem.setAttributeNS(namespace, attribute, value); + let attr_node = elem.getAttributeNodeNS(namespace, attribute); + assert_equals(attr_node.value + "", expected); +} + +function assert_throws_no_trusted_type_set_ns(tag, attribute, value) { + let elem = document.createElement(tag); + assert_throws_js(TypeError, _ => { + elem.setAttributeNS(namespace, attribute, value); + }); +} + +function assert_element_accepts_non_trusted_type_set_ns(tag, attribute, value, expected) { + let elem = document.createElement(tag); + elem.setAttributeNS(namespace, attribute, value); + let attr_node = elem.getAttributeNodeNS(namespace, attribute); + assert_equals(attr_node.value + "", expected); +} diff --git a/testing/web-platform/tests/trusted-types/support/navigation-report-only-support.html b/testing/web-platform/tests/trusted-types/support/navigation-report-only-support.html new file mode 100644 index 0000000000..5f7856fabb --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/navigation-report-only-support.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<head> +</head> +<body> +<p>Support page for trusted-types-navigation-report-only.*.html tests.</p> +<a id="anchor" href="#">link</a> +<script> + const params = new URLSearchParams(location.search); + if (!!params.get("defaultpolicy")) { + trustedTypes.createPolicy("default", { + createScript: s => s.replace("continue", "defaultpolicywashere"), + }); + } + + function bounceEventToOpener(e) { + const msg = {}; + for (const field of ["effectiveDirective", "sample", "type"]) { + msg[field] = e[field]; + } + msg["uri"] = location.href; + window.opener.postMessage(msg, "*"); + } + + // If a navigation is blocked by Trusted Types, we expect this window to + // throw a SecurityPolicyViolationEvent. If it's not blocked, we expect the + // loaded frame to through DOMContentLoaded. In either case there should be + // _some_ event that we can expect. + document.addEventListener("DOMContentLoaded", bounceEventToOpener); + document.addEventListener("securitypolicyviolation", bounceEventToOpener); + + // Navigate to the non-report-only version of the test. That has the same + // event listening setup as this, but is a different target URI. + const target_script = `location.href='${location.href.replace("-report-only", "") + "#continue"}';`; + const target = `javascript:"<script>${target_script}</scri${""}pt>"`; + + // Navigate the anchor, but only after the content is loaded (so that we + // won't disturb delivery of that event to the opener. + const anchor = document.getElementById("anchor"); + anchor.href = target; + + if (!!params.get("frame")) { + const frame = document.createElement("iframe"); + frame.src = "frame-without-trusted-types.html"; + frames.name = "frame"; + document.body.appendChild(frame); + anchor.target = "frame"; + } + + if (!location.hash) { + document.addEventListener("DOMContentLoaded", _ => anchor.click()); + } +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/support/navigation-report-only-support.html.headers b/testing/web-platform/tests/trusted-types/support/navigation-report-only-support.html.headers new file mode 100644 index 0000000000..4c1ff15e16 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/navigation-report-only-support.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/support/navigation-support.html b/testing/web-platform/tests/trusted-types/support/navigation-support.html new file mode 100644 index 0000000000..5e02e6d4bf --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/navigation-support.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<head> +</head> +<body> +<p>Support page for trusted-types-navigation.*.html tests.</p> +<a id="anchor" href="#">link</a> +<script> + const params = new URLSearchParams(location.search); + if (!!params.get("defaultpolicy")) { + trustedTypes.createPolicy("default", { + createScript: s => s.replace("continue", "defaultpolicywashere"), + }); + } + + function bounceEventToOpener(e) { + const msg = {}; + for (const field of ["effectiveDirective", "sample", "type"]) { + msg[field] = e[field]; + } + msg["uri"] = location.href; + window.opener.postMessage(msg, "*"); + } + + // If a navigation is blocked by Trusted Types, we expect this window to + // throw a SecurityPolicyViolationEvent. If it's not blocked, we expect the + // loaded frame to through DOMContentLoaded. In either case there should be + // _some_ event that we can expect. + document.addEventListener("DOMContentLoaded", bounceEventToOpener); + document.addEventListener("securitypolicyviolation", bounceEventToOpener); + + // We'll use a javascript:-url to navigate to ourselves, so that we can + // re-use the messageing mechanisms above. In order to not end up in a loop, + // we'll only click if we don't find fragment in the current URL. + const target_script = `location.href='${location.href}&continue';`; + const target = `javascript:"<script>${target_script}</scri${""}pt>"`; + + const anchor = document.getElementById("anchor"); + anchor.href = target; + + if (!!params.get("frame")) { + const frame = document.createElement("iframe"); + frame.src = "frame-without-trusted-types.html"; + frames.name = "frame"; + document.body.appendChild(frame); + anchor.target = "frame"; + } + + if (!location.hash) + document.addEventListener("DOMContentLoaded", _ => anchor.click()); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/support/navigation-support.html.headers b/testing/web-platform/tests/trusted-types/support/navigation-support.html.headers new file mode 100644 index 0000000000..604e765da4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/navigation-support.html.headers @@ -0,0 +1 @@ +Content-Security-Policy: require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/support/set-inner-html.js b/testing/web-platform/tests/trusted-types/support/set-inner-html.js new file mode 100644 index 0000000000..45053d43e3 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/set-inner-html.js @@ -0,0 +1,3 @@ +function setInnerHtml(element, value) { + element.innerHTML = value; +} diff --git a/testing/web-platform/tests/trusted-types/support/worker.js b/testing/web-platform/tests/trusted-types/support/worker.js new file mode 100644 index 0000000000..4079f7e9c7 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/support/worker.js @@ -0,0 +1 @@ +self.result = "Pass"; diff --git a/testing/web-platform/tests/trusted-types/trusted-types-createHTMLDocument.html b/testing/web-platform/tests/trusted-types/trusted-types-createHTMLDocument.html new file mode 100644 index 0000000000..e4af2eb590 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-createHTMLDocument.html @@ -0,0 +1,71 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <meta http-equiv="Content-Security-Policy" + content="trusted-types policy default 'allow-duplicates'; require-trusted-types-for 'script'"> +</head> +<body> +<script> + +// Test Trusted Types in document types other than the main document, such as +// documents created by createHTMLDocument or XHR requests. + +function create_XHR_document() { + return new Promise(resolve => { + var xhr = new XMLHttpRequest(); + xhr.onload = _ => { resolve(xhr.response); }; + xhr.open("GET", 'data:text/html,<title>aaa</title>'); + xhr.responseType = "document"; + xhr.send(); + }); +} + +const doc_types = { + "document": _ => document, + "createHTMLDocument": _ => document.implementation.createHTMLDocument(""), + "DOMParser": _ => (new DOMParser).parseFromString(trustedTypes.emptyHTML, "text/html"), + "XHR": create_XHR_document, +} + +function doc_test(doc_type, test_fn, description) { + promise_test(t => { + return Promise.resolve(doc_types[doc_type]()).then(test_fn); + }, `${description} (${doc_type})`); +} + +for (let doc_type in doc_types) { + doc_test(doc_type, doc => { + assert_throws_js(TypeError, + _ => { doc.createElement("script").textContent = "2+2"; }); + }, "Trusted Type assignment is blocked." ); + + doc_test(doc_type, doc => { + const policy = trustedTypes.createPolicy("policy", {createHTML: x => x }); + const value = policy.createHTML("hello"); + doc.body.innerHTML = value; + assert_equals(doc.body.textContent, "hello"); + assert_throws_js(TypeError, + _ => { doc.body.innerHTML = "world"; }); + }, "Trusted Type instances created in the main doc can be used."); +} + +// Create default policy (applies to all subsequent tests). +// Wrapped in a promise_test so that it won't interfere with the previous tests +// (which hanve't yet run). +promise_test(t => { + return new Promise(resolve => { + trustedTypes.createPolicy("default", + { createHTML: s => s + " [default]" }); + resolve(); + }); +}, "Install default policy.") + +for (let doc_type in doc_types) { + doc_test(doc_type, doc => { + doc.body.innerHTML = "shouldpass"; + assert_equals(doc.body.textContent, "shouldpass [default]"); + }, "Default policy applies."); +} +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list-report-only.html b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list-report-only.html new file mode 100644 index 0000000000..46ca2edb6f --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list-report-only.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +// We assume the following header has been set: +// Content-Security-Policy-Report-Only: trusted-types a b c +// (CSP-Report-Only headers can't be set in <meta> tags.) + +test(t => { + trustedTypes.createPolicy("a", {}); + trustedTypes.createPolicy("a", {}); + trustedTypes.createPolicy("b", {}); + trustedTypes.createPolicy("d", {}); +}, "TrustedTypePolicyFactory and policy list in CSP."); + +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list-report-only.html.headers b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list-report-only.html.headers new file mode 100644 index 0000000000..b6608515aa --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list-report-only.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: trusted-types a b c diff --git a/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list.html b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list.html new file mode 100644 index 0000000000..afb2f5f7c4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-list.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <meta http-equiv="Content-Security-Policy" content="trusted-types a b c*"> +</head> +<body> +<script> +test(t => { + trustedTypes.createPolicy("a", {}), + assert_throws_js(TypeError, + _ => trustedTypes.createPolicy("a", {}), + "Duplicate name"); + + trustedTypes.createPolicy("b", {}), + + assert_throws_js(TypeError, + _ => trustedTypes.createPolicy("d", {}), + "Invalid name."); +}, "TrustedTypePolicyFactory and policy list in CSP."); + +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-without-enforcement.html b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-without-enforcement.html new file mode 100644 index 0000000000..afef457a9c --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names-without-enforcement.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +const policy = { createHTML: a => a }; + +test(t => { + // The spec demands that duplicate policies are allowed when TT is not + // enforced in the CSP. + let a = trustedTypes.createPolicy("duplicatename", policy); + let b = trustedTypes.createPolicy("duplicatename", policy); + assert_not_equals(a, b); + assert_true(a instanceof TrustedTypePolicy); + assert_true(b instanceof TrustedTypePolicy); +}, "createPolicy - duplicate policies are allowed when Trusted Types are not enforced."); + +test(t => { + // The spec demands that duplicate "default" policy creation fails, even + // when TT is not enforced. + let p = trustedTypes.createPolicy("default", policy); + assert_true(p instanceof TrustedTypePolicy); + + // The second instance should throw: + assert_throws_js(TypeError, _ => trustedTypes.createPolicy("default", policy)); +}, "createPolicy - duplicate \"default\" policy is never allowed."); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names.html b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names.html new file mode 100644 index 0000000000..decce53564 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-duplicate-names.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <meta http-equiv="Content-Security-Policy" + content="trusted-types 'allow-duplicates' duplicatename default"> +</head> +<body> +<script> +test(t => { + // We expect neither of these to throw. + let a = trustedTypes.createPolicy("duplicatename", { createHTML: _ => "a" }); + let b = trustedTypes.createPolicy("duplicatename", { createHTML: _ => "b" }); + + // Both should have worked. They should still be separate functions. + assert_not_equals(a, b); + assert_equals("" + a.createHTML(""), "a"); + assert_equals("" + b.createHTML(""), "b"); + + let def = trustedTypes.createPolicy("default", {}); + assert_throws_js(TypeError, + _ => trustedTypes.createPolicy("default", {})); +}, "policy - duplicate names"); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html new file mode 100644 index 0000000000..2b0922d212 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<head> + <script nonce="123" src="/resources/testharness.js"></script> + <script nonce="123"src="/resources/testharnessreport.js"></script> + <script nonce="123"src="/content-security-policy/support/testharness-helper.js"></script> +</head> +<body> + <script nonce="123"> + // CSP insists the "trusted-types: ..." directives are deliverd as headers + // (rather than as "<meta http-equiv" tags). This test assumes the following + // headers are set in the .headers file: + // + // Content-Security-Policy: script-src 'unsafe-inline'; report-uri ... + // Content-Security-Policy: object-src 'none' + // Content-Security-Policy: require-trusted-types-for 'script' + // + // The second rule is there so we can provoke a CSP violation report at will. + // The intent is that in order to test that a violation has *not* been thrown + // (and without resorting to abominations like timeouts), we force a *another* + // CSP violation (by violating the object-src rule) and when that event is + // processed we can we sure that an earlier event - if it indeed occurred - + // must have already been processed. + + // Return function that returns a promise that resolves on the given + // violation report. + // how_many - how many violation events are expected. + // filter_arg - iff function, call it with the event object. + // Else, string-ify and compare against event.originalPolicy. + function promise_violation(filter_arg) { + return _ => new Promise((resolve, reject) => { + function handler(e) { + let matches = (filter_arg instanceof Function) + ? filter_arg(e) + : (e.originalPolicy.includes(filter_arg)); + if (matches) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + document.addEventListener("securitypolicyviolation", handler); + }); + } + + // Like assert_throws_*, but we don't care about the exact error. We just want + // to run the code and continue. + function expect_throws(fn) { + try { fn(); assert_unreached(); } catch (err) { /* ignore */ } + } + + // A sample policy we use to test trustedTypes.createPolicy behaviour. + const id = x => x; + const a_policy = { + createHTML: id, + createScriptURL: id, + createURL: id, + createScript: id, + }; + + const scriptyPolicy = trustedTypes.createPolicy('allowEval', a_policy); + + // Provoke/wait for a CSP violation, in order to be sure that all previous + // CSP violations have been delivered. + function promise_flush() { + return promise_violation("object-src 'none'"); + } + function flush() { + expect_throws(_ => { + var o = document.createElement('object'); + o.type = "application/x-shockwave-flash"; + document.body.appendChild(o); + }); + } + + window.script_run_beacon = 'never_overwritten'; + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(promise_flush()); + expect_throws(_ => eval('script_run_beacon="should not run"')); + assert_equals(script_run_beacon, 'never_overwritten'); + flush(); + return p; + }, "Trusted Type violation report: evaluating a string violates both script-src and trusted-types."); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("script-src")) + .then(promise_flush()); + expect_throws(_ => eval(scriptyPolicy.createScript('script_run_beacon="i ran"'))); + flush(); + assert_not_equals(script_run_beacon, 'i ran'); // Code did not run. + return p; + }, "Trusted Type violation report: evaluating a Trusted Script violates script-src."); + + promise_test(t => { + trustedTypes.createPolicy('default', { + createScript: s => s.replace('payload', 'default policy'), + }, true); + let p = Promise.resolve() + .then(promise_violation((e) => + e.effectiveDirective.includes('script-src') && + e.sample.includes("default policy"))) + .then(promise_flush()); + expect_throws(_ => eval('script_run_beacon="payload"')); // script-src will block. + assert_not_equals(script_run_beacon, 'default policy'); // Code did not run. + flush(); + return p; + }, "Trusted Type violation report: script-src restrictions apply after the default policy runs."); + + </script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.headers b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.headers new file mode 100644 index 0000000000..2bdd8a7d1a --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-no-unsafe-eval.html.headers @@ -0,0 +1,3 @@ +Content-Security-Policy: script-src http: https: 'nonce-123' 'report-sample' +Content-Security-Policy: object-src 'none' +Content-Security-Policy: require-trusted-types-for 'script' diff --git a/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-report-only.html b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-report-only.html new file mode 100644 index 0000000000..4e8ac5a2f4 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-report-only.html @@ -0,0 +1,107 @@ +<!DOCTYPE html> +<head> + <script nonce="123" src="/resources/testharness.js"></script> + <script nonce="123"src="/resources/testharnessreport.js"></script> + <script nonce="123"src="/content-security-policy/support/testharness-helper.js"></script> +</head> +<body> + <script nonce="123"> + // CSP insists the "trusted-types: ..." directives are deliverd as headers + // (rather than as "<meta http-equiv" tags). This test assumes the following + // headers are set in the .headers file: + // + // Content-Security-Policy: script-src 'unsafe-inline' 'unsafe-eval'; report-uri ... + // Content-Security-Policy: object-src 'none' + // Content-Security-Policy-Report-Only: require-trusted-types-for 'script' + // + // The last rule is there so we can provoke a CSP violation report at will. + // The intent is that in order to test that a violation has *not* been thrown + // (and without resorting to abominations like timeouts), we force a *another* + // CSP violation (by violating the img-src rule) and when that event is + // processed we can we sure that an earlier event - if it indeed occurred - + // must have already been processed. + + // Return function that returns a promise that resolves on the given + // violation report. + // + // filter_arg - iff function, call it with the event object. + // Else, string-ify and compare against event.originalPolicy. + function promise_violation(filter_arg) { + return _ => new Promise((resolve, reject) => { + function handler(e) { + let matches = (filter_arg instanceof Function) + ? filter_arg(e) + : (e.originalPolicy.includes(filter_arg)); + if (matches) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + document.addEventListener("securitypolicyviolation", handler); + }); + } + + // Like assert_throws_*, but we don't care about the exact error. We just want + // to run the code and continue. + function expect_throws(fn) { + try { fn(); assert_unreached(); } catch (err) { /* ignore */ } + } + + // A sample policy we use to test trustedTypes.createPolicy behaviour. + const id = x => x; + const a_policy = { + createHTML: id, + createScriptURL: id, + createURL: id, + createScript: id, + }; + + const scriptyPolicy = trustedTypes.createPolicy('allowEval', a_policy); + + // Provoke/wait for a CSP violation, in order to be sure that all previous + // CSP violations have been delivered. + function promise_flush() { + return promise_violation("object-src 'none'"); + } + function flush() { + expect_throws(_ => { + var o = document.createElement('object'); + o.type = "application/x-shockwave-flash"; + document.body.appendChild(o); + }); + } + + window.script_run_beacon = 'vanilla'; + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(promise_flush()); + eval('script_run_beacon="report-only-does-not-stop"'); + assert_equals(script_run_beacon, 'report-only-does-not-stop'); + flush(); + return p; + }, "Trusted Type violation report: evaluating a string."); + + promise_test(t => { + let p = promise_flush()(); + eval(scriptyPolicy.createScript('script_run_beacon="trusted-script-ok"')); + flush(); + assert_equals(script_run_beacon, 'trusted-script-ok'); + return p; + }, "Trusted Type violation report: evaluating a Trusted Script."); + + promise_test(t => { + trustedTypes.createPolicy('default', { + createScript: s => s.replace('payload', 'default policy'), + }, true); + let p = promise_flush()(); + eval('script_run_beacon="payload"'); + assert_equals(script_run_beacon, 'default policy'); + flush(); + return p; + }, "Trusted Type violation report: default policy runs in report-only mode."); + + </script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-report-only.html.headers b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-report-only.html.headers new file mode 100644 index 0000000000..d212eda5ef --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting-report-only.html.headers @@ -0,0 +1,4 @@ +Content-Security-Policy: script-src http: https: 'nonce-123' 'unsafe-eval' +Content-Security-Policy: object-src 'none' +Content-Security-Policy-Report-Only: require-trusted-types-for 'script' + diff --git a/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting.html b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting.html new file mode 100644 index 0000000000..73bb011349 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting.html @@ -0,0 +1,102 @@ +<!DOCTYPE html> +<head> + <script nonce="123" src="/resources/testharness.js"></script> + <script nonce="123" src="/resources/testharnessreport.js"></script> +</head> +<body> + <script nonce="123"> + // CSP insists the "trusted-types: ..." directives are deliverd as headers + // (rather than as "<meta http-equiv" tags). This test assumes the following + // headers are set in the .headers file: + // + // Content-Security-Policy: script-src 'unsafe-inline' 'unsafe-eval'; report-uri ... + // Content-Security-Policy: object-src 'none' + // Content-Security-Policy: require-trusted-types-for 'script' + // + // The last rule is there so we can provoke a CSP violation report at will. + // The intent is that in order to test that a violation has *not* been thrown + // (and without resorting to abominations like timeouts), we force a *another* + // CSP violation (by violating the img-src rule) and when that event is + // processed we can we sure that an earlier event - if it indeed occurred - + // must have already been processed. + + // Return function that returns a promise that resolves on the given + // violation report. + function promise_violation(filter_arg) { + return _ => new Promise((resolve, reject) => { + function handler(e) { + let matches = e.originalPolicy.includes(filter_arg); + if (matches) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + document.addEventListener("securitypolicyviolation", handler); + }); + } + + // Like assert_throws_*, but we don't care about the exact error. We just want + // to run the code and continue. + function expect_throws(fn) { + try { fn(); assert_unreached(); } catch (err) { /* ignore */ } + } + + // A sample policy we use to test trustedTypes.createPolicy behaviour. + const id = x => x; + const a_policy = { + createHTML: id, + createScriptURL: id, + createURL: id, + createScript: id, + }; + const scriptyPolicy = trustedTypes.createPolicy('allowEval', a_policy); + + // Provoke/wait for a CSP violation, in order to be sure that all previous + // CSP violations have been delivered. + function promise_flush() { + return promise_violation("object-src 'none'"); + } + function flush() { + expect_throws(_ => { + var o = document.createElement('object'); + o.type = "application/x-shockwave-flash"; + document.body.appendChild(o); + }); + } + + promise_test(t => { + let beacon = 'never_overwritten'; + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(promise_flush()); + assert_throws_js(EvalError, + _ => eval('beacon="should not run"')); + assert_equals(beacon, 'never_overwritten'); + flush(); + return p; + }, "Trusted Type violation report: evaluating a string."); + + promise_test(t => { + let beacon = 'never_overwritten2'; + let p = promise_flush()(); + eval(scriptyPolicy.createScript('beacon="i ran"')); + assert_equals(beacon, 'i ran'); + flush(); + return p; + }, "Trusted Type violation report: evaluating a Trusted Script."); + + promise_test(t => { + let beacon = 'never_overwritten'; + trustedTypes.createPolicy('default', { + createScript: s => s.replace('payload', 'default policy'), + }, true); + let p = promise_flush()(); + eval('beacon="payload"'); + assert_equals(beacon, 'default policy'); + flush(); + return p; + }, "Trusted Type violation report: default policy transforms the script before CSP checks runs."); + + </script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting.html.headers b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting.html.headers new file mode 100644 index 0000000000..8989345e72 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-eval-reporting.html.headers @@ -0,0 +1,4 @@ +Content-Security-Policy: script-src http: https: 'nonce-123' 'unsafe-eval' +Content-Security-Policy: object-src 'none' +Content-Security-Policy: require-trusted-types-for 'script' + diff --git a/testing/web-platform/tests/trusted-types/trusted-types-event-handlers.html b/testing/web-platform/tests/trusted-types/trusted-types-event-handlers.html new file mode 100644 index 0000000000..9dd7133cbb --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-event-handlers.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<head> + <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> +const element = document.createElement("div"); + +[ + "onclick", + "onchange", + "onfocus", + "oNclick", + "OnClIcK" +].forEach(name => { + test(t => { + assert_throws_js(TypeError, + _ => element.setAttribute(name, "2+2")); + }, `Event handler ${name} should be blocked.`); +}); + +[ + "one", + "oNe", + "onIcon", + "offIcon", + "blubb" +].forEach(name => { + test(t => { + element.setAttribute(name, "2+2"); + }, `Non-event handler ${name} should not be blocked.`); +}); + +// We'd like to be sure we're not missing anything. Let's "query" an HTML +// element about which attributes it knows. +const div = document.createElement("div"); +for(name in div.__proto__) { + // This captures all "on{foo}" handlers, but not "on" itself, which is an IDL + // attribute that returns an Observable. + const should_be_event_handler = name.startsWith("on") && name !== "on"; + if (should_be_event_handler) { + test(t => { + assert_throws_js(TypeError, + _ => element.setAttribute(name, "2+2")); + }, `Event handler div.${name} should be blocked.`); + } else { + test(t => { + element.setAttribute(name, "2+2"); + }, `Non-event handler div.${name} should not be blocked.`); + } +} +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-from-literal.html b/testing/web-platform/tests/trusted-types/trusted-types-from-literal.html new file mode 100644 index 0000000000..a7d5659e16 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-from-literal.html @@ -0,0 +1,118 @@ +<!DOCTYPE html> +<head> +<link rel="author" title="Daniel Vogelheim" href="mailto:vogelheim@chromium.org"></link> +<link rel="help" href="https://w3c.github.io/trusted-types/dist/spec/"></link> +<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> +[ TrustedHTML, TrustedScript, TrustedScriptURL ].forEach(type => { + + test(t => { + assert_true("fromLiteral" in type); + }, `${type.name}.fromLiteral is supported`); + + test(t => { + const c = type.fromLiteral`abc`; + assert_true(c instanceof type); + assert_equals(c.toString(), "abc"); + }, `${type.name}.fromLiteral: Basic string literal works.`); + + test(t => { + const c = type.fromLiteral``; + assert_true(c instanceof type); + assert_equals(c.toString(), ""); + }, `${type.name}.fromLiteral: Empty string literal works.`); + + test(t => { + const c = type.fromLiteral`\u{1f4a9}`; + assert_true(c instanceof type); + assert_equals(c.toString(), "\u{1f4a9}"); + }, `${type.name}.fromLiteral: A very important emoji works.`); + + test(t => { + const c = type.fromLiteral`A Multiline + string + works.`; + assert_true(c instanceof type); + assert_true(c.toString().includes("\n")); + }, `${type.name}.fromLiteral: Multi-line string literal works.`); + + test(t => { + const tag = type.fromLiteral.bind(type); + const c = tag`abc`; + assert_true(c instanceof type); + assert_equals(c.toString(), "abc"); + }, `${type.name}.fromLiteral: Bound method works.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral("abc"); + }); + }, `${type.name}.fromLiteral: String throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral(null); + }); + }, `${type.name}.fromLiteral: null throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral(undefined); + }); + }, `${type.name}.fromLiteral: undefined throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral({}); + }); + }, `${type.name}.fromLiteral: Object throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral`Hello ${2+3} world` + }); + }, `${type.name}.fromLiteral: template literal with expression throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral(["abc"]); + }); + }, `${type.name}.fromLiteral: non-literal throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral(Object.freeze(["abc"])); + }); + }, `${type.name}.fromLiteral: frozen non-literal throws.`); + + test(t => { + assert_throws_js(TypeError, _ => { + type.fromLiteral(["abc", "def"], "xxx"); + }); + }, `${type.name}.fromLiteral: non-literal with param throws.`); + +}); + +// TrustedHTML requires normalization of the value. Let's test that TrustedHTML +// (and only TrustedHTML) does this. +test(t => { + const tag = TrustedHTML.fromLiteral.bind(TrustedHTML); + assert_equals(tag`<b>`.toString(), "<b></b>"); +}, "TrustedHTML.fromLiteral: Normalization of value works."); + +test(t => { + const tag = TrustedScript.fromLiteral.bind(TrustedScript); + assert_equals(tag`<b>`.toString(), "<b>"); +}, "TrustedScript.fromLiteral: No normalization of value occurs."); + +test(t => { + const tag = TrustedScriptURL.fromLiteral.bind(TrustedScriptURL); + assert_equals(tag`<b>`.toString(), "<b>"); +}, "TrustedScriptURL.fromLiteral: No normalization of value occurs."); + +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-navigation.html b/testing/web-platform/tests/trusted-types/trusted-types-navigation.html new file mode 100644 index 0000000000..2113711902 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-navigation.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + function expectMessage(filter) { + return new Promise(resolve => { + window.addEventListener("message", e => { if (filter(e)) resolve(); }); + }); + } + + function expectViolationAsMessage(sample) { + const filter = e => ((e.data.effectiveDirective == "require-trusted-types-for" || + e.data.effectiveDirective == "trusted-types") && + (!sample || e.data.sample.startsWith(sample))); + return new expectMessage(filter); + } + + function expectLoadedAsMessage(uri) { + const filter = e => (e.data.type == "DOMContentLoaded" && + (!uri || e.data.uri.endsWith(uri))); + return new expectMessage(filter); + } + + function openWindow(test, uri) { + const win = window.open(uri); + test.add_cleanup(_ => win.close()); + } + + promise_test(t => { + openWindow(t, "support/navigation-support.html"); + return Promise.all([ + expectLoadedAsMessage("navigation-support.html"), + expectViolationAsMessage("Location href"), + ]); + }, "Navigate a window with javascript:-urls in enforcing mode."); + + promise_test(t => { + openWindow(t, "support/navigation-support.html?defaultpolicy=1"); + return Promise.all([ + expectLoadedAsMessage("navigation-support.html?defaultpolicy=1"), + expectLoadedAsMessage("navigation-support.html?defaultpolicy=1&defaultpolicywashere"), + ]); + }, "Navigate a window with javascript:-urls w/ default policy in enforcing mode."); + + promise_test(t => { + const page = "navigation-report-only-support.html" + openWindow(t, `support/${page}`); + return Promise.all([ + expectLoadedAsMessage(page), + expectLoadedAsMessage("navigation-support.html#continue"), + ]); + }, "Navigate a window with javascript:-urls in report-only mode."); + + promise_test(t => { + const page = "navigation-report-only-support.html?defaultpolicy=1"; + openWindow(t, `support/${page}`); + return Promise.all([ + expectLoadedAsMessage(page), + expectLoadedAsMessage("navigation-support.html?defaultpolicy=1#defaultpolicywashere"), + ]); + }, "Navigate a window with javascript:-urls w/ default policy in report-only mode."); + + promise_test(t => { + openWindow(t, "support/navigation-support.html?frame=1"); + return Promise.all([ + expectLoadedAsMessage("navigation-support.html?frame=1"), + expectViolationAsMessage("Location href"), + ]); + }, "Navigate a frame with javascript:-urls in enforcing mode."); + + promise_test(t => { + openWindow(t, "support/navigation-support.html?defaultpolicy=1&frame=1"); + return Promise.all([ + expectLoadedAsMessage("navigation-support.html?defaultpolicy=1&frame=1"), + expectLoadedAsMessage("navigation-support.html?defaultpolicy=1&frame=1&defaultpolicywashere"), + ]); + }, "Navigate a frame with javascript:-urls w/ default policy in enforcing mode."); + + promise_test(t => { + const page = "navigation-report-only-support.html?frame=1" + openWindow(t, `support/${page}`); + return Promise.all([ + expectLoadedAsMessage(page), + expectLoadedAsMessage("navigation-support.html?frame=1#continue"), + ]); + }, "Navigate a frame with javascript:-urls in report-only mode."); + + promise_test(t => { + const page = "navigation-report-only-support.html?defaultpolicy=1&frame=1"; + openWindow(t, `support/${page}`); + return Promise.all([ + expectLoadedAsMessage(page), + expectLoadedAsMessage("navigation-support.html?defaultpolicy=1&frame=1#defaultpolicywashere"), + ]); + }, "Navigate a frame with javascript:-urls w/ default policy in report-only mode."); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-report-only.html b/testing/web-platform/tests/trusted-types/trusted-types-report-only.html new file mode 100644 index 0000000000..fcb7784116 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-report-only.html @@ -0,0 +1,86 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/content-security-policy/support/testharness-helper.js"></script> +</head> +<body> + + <!-- Some elements for the tests to act on. --> + <div id="div"></div> + <script id="script-src" src=""></script> + <script id="script"></script> + <script id="script2"></script> + + <script> + // CSP insists the "trusted-types: ..." directives are deliverd as headers + // (rather than as "meta http-equiv" tags). This test assumes the following + // headers are set in the .headers file: + // + // Content-Security-Policy-Report-Only: trusted-types ...; report-uri ... + + // Return function that returns a promise that resolves on the given + // violation report. + function expect_violation(filter) { + return new Promise((resolve, reject) => { + function handler(e) { + if (e.originalPolicy.includes(filter)) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + document.addEventListener("securitypolicyviolation", handler); + }); + } + + // A sample policy we use to test trustedTypes.createPolicy behaviour. + const id = x => x; + const policy = trustedTypes.createPolicy("two", { + createHTML: id, + createScriptURL: id, + createScript: id, + }); +/* + promise_test(t => { + let p = expect_violation("trusted-types two"); + document.getElementById("script").src = "#abc"; + assert_true(document.getElementById("script").src.endsWith("#abc")); + return p; + }, "Trusted Type violation report-only: assign string to script url"); +*/ + + promise_test(t => { + let p = expect_violation("trusted-types two"); + document.getElementById("div").innerHTML = "abc"; + assert_equals(document.getElementById("div").textContent, "abc"); + return p; + }, "Trusted Type violation report-only: assign string to html"); + + promise_test(t => { + let p = expect_violation("trusted-types two"); + document.getElementById("script-src").src = "#"; + assert_true(document.getElementById("script-src").src.endsWith("#")); + return p; + }, "Trusted Type violation report-only: assign string to script.src"); + + promise_test(t => { + let p = expect_violation("trusted-types two"); + document.getElementById("script").innerHTML = "con" + "sole.log('Hello');"; + assert_true(document.getElementById("script").textContent.startsWith("consol")); + return p; + }, "Trusted Type violation report-only: assign string to script content"); + + promise_test(t => { + let p = expect_violation("trusted-types two"); + document.getElementById("script").src = "#def"; + return p.then(report => { + assert_equals(report.documentURI, "" + window.location); + assert_equals(report.disposition, "report"); + assert_equals(report.effectiveDirective, "require-trusted-types-for"); + assert_equals(report.violatedDirective, "require-trusted-types-for"); + assert_true(report.originalPolicy.startsWith("trusted-types two;")); + }); + }, "Trusted Type violation report: check report contents"); + </script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-report-only.html.headers b/testing/web-platform/tests/trusted-types/trusted-types-report-only.html.headers new file mode 100644 index 0000000000..857a8b31e8 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-report-only.html.headers @@ -0,0 +1 @@ +Content-Security-Policy-Report-Only: trusted-types two; report-uri /content-security-policy/resources/dummy-report.php; require-trusted-types-for 'script'; diff --git a/testing/web-platform/tests/trusted-types/trusted-types-reporting-check-report.html b/testing/web-platform/tests/trusted-types/trusted-types-reporting-check-report.html new file mode 100644 index 0000000000..ae5ac25052 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-reporting-check-report.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <title>Check Trusted Type violation reports</title> + +<!-- We assume these HTTP headers are set on the request: + + Set-Cookie: trusted-types-reporting-check-report={{$id:uuid()}}; Path=/trusted-types/ + Content-Security-Policy-Report-Only: \ + trusted-types one two; \ + report-uri /reporting/resources/report.py?op=put&reportID={{$id}} +--> +</head> +<body> + <script> + trustedTypes.createPolicy("three", {}); + </script> + <script async defer src='../content-security-policy/support/checkReport.sub.js?reportField=violated-directive&reportValue=trusted-types'></script> +</body> +</html> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-reporting-check-report.html.sub.headers b/testing/web-platform/tests/trusted-types/trusted-types-reporting-check-report.html.sub.headers new file mode 100644 index 0000000000..c055bdc656 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-reporting-check-report.html.sub.headers @@ -0,0 +1,6 @@ +Expires: Mon, 26 Jul 1997 05:00:00 GMT +Cache-Control: no-store, no-cache, must-revalidate +Cache-Control: post-check=0, pre-check=0, false +Pragma: no-cache +Set-Cookie: trusted-types-reporting-check-report={{$id:uuid()}}; Path=/trusted-types/ +Content-Security-Policy-Report-Only: trusted-types one two; report-uri /reporting/resources/report.py?op=put&reportID={{$id}} diff --git a/testing/web-platform/tests/trusted-types/trusted-types-reporting.html b/testing/web-platform/tests/trusted-types/trusted-types-reporting.html new file mode 100644 index 0000000000..9db307db60 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-reporting.html @@ -0,0 +1,279 @@ +<!DOCTYPE html> +<head> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/content-security-policy/support/testharness-helper.js"></script> +</head> +<body> + <script> + // CSP insists the "trusted-types: ..." directives are delivered as headers + // (rather than as "<meta http-equiv" tags). This test assumes the following + // headers are set in the .headers file: + // + // Content-Security-Policy: trusted-types one + // Content-Security-Policy-Report-Only: trusted-types two; report-uri ... + // Content-Security-Policy: object-src 'none' + // Content-Security-Policy: default-src * 'unsafe-inline' + // + // The third rule is there so we can provoke a CSP violation report at will. + // The intent is that in order to test that a violation has *not* been thrown + // (and without resorting to abominations like timeouts), we force a *another* + // CSP violation (by violating the object-src rule) and when that event is + // processed we can we sure that an earlier event - if it indeed occurred - + // must have already been processed. + // + // The last rule allows all scripting except 'unsafe-eval', so we can also + // test reporting of this case. + + const url = "" + document.location; + + // Return function that returns a promise that resolves on the given + // violation report. + // + // filter_arg - iff function, call it with the event object. + // Else, string-ify and compare against event.originalPolicy. + function promise_violation(filter_arg) { + return _ => new Promise((resolve, reject) => { + function handler(e) { + let matches = (filter_arg instanceof Function) + ? filter_arg(e) + : (e.originalPolicy.includes(filter_arg)); + if (matches) { + document.removeEventListener("securitypolicyviolation", handler); + e.stopPropagation(); + resolve(e); + } + } + document.addEventListener("securitypolicyviolation", handler); + }); + } + + // Like assert_throws_*, but we don't care about the exact error. We just want + // to run the code and continue. + function expect_throws(fn) { + try { fn(); assert_unreached(); } catch (err) { /* ignore */ } + } + + // Test the "sample" field of the event. + // TODO(vogelheim): The current set of tests allows for more variance in the + // sample reports than the current spec draft does. Once the spec has + // been finalized, we should clamp this down to check byte-for-byte + // against the values mandated by the spec. + + function expect_sample(s) { return e => { + assert_true(e.sample.includes(s), + `expected "${e.sample}" to include "${s}".`); + return e; + } } + + function expect_blocked_uri(s) { return e => { + assert_equals(e.blockedURI, s, + `expected "${e.blockedURI}" to be "${s}".`); + return e; + } } + + // A sample policy we use to test trustedTypes.createPolicy behaviour. + const id = x => x; + const a_policy = { + createHTML: id, + createScriptURL: id, + createScript: id, + }; + + // Provoke/wait for a CSP violation, in order to be sure that all previous + // CSP violations have been delivered. + function promise_flush() { + return promise_violation("object-src 'none'"); + } + function flush() { + expect_throws(_ => { + var o = document.createElement('object'); + o.type = "application/x-shockwave-flash"; + document.body.appendChild(o); + }); + } + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("trusted-types one")) + .then(promise_violation("trusted-types two")) + .then(expect_sample("three")) + .then(expect_blocked_uri("trusted-types-policy")) + .then(promise_flush()); + expect_throws(_ => trustedTypes.createPolicy("three", a_policy)); + flush(); + return p; + }, "Trusted Type violation report: creating a forbidden policy."); + + promise_test(t => { + let p = promise_flush()(); + expect_throws(_ => trustedTypes.createPolicy("two", a_policy)); + flush(); + return p; + }, "Trusted Type violation report: creating a report-only-forbidden policy."); + + // policy_one is set below, and used in several tests further down. + let policy_one = null; + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("trusted-types two")) + .then(promise_flush()); + policy_one = trustedTypes.createPolicy("one", a_policy); + flush(); + return p; + }, "Trusted Type violation report: creating a forbidden-but-not-reported policy."); + + promise_test(t => { + let p = promise_violation("require-trusted-types-for 'script'")(); + expect_throws(_ => document.getElementById("script").src = url); + return p; + }, "Trusted Type violation report: assign string to script url"); + + promise_test(t => { + let p = promise_violation("require-trusted-types-for 'script'")(); + expect_throws(_ => document.getElementById("div").innerHTML = "abc"); + return p; + }, "Trusted Type violation report: assign string to html"); + + promise_test(t => { + let p = promise_flush()(); + document.getElementById("script").text = policy_one.createScript("2+2;"); + flush(); + return p; + }, "Trusted Type violation report: assign trusted script to script; no report"); + + promise_test(t => { + let p = promise_flush()(); + document.getElementById("div").innerHTML = policy_one.createHTML("abc"); + flush(); + return p; + }, "Trusted Type violation report: assign trusted HTML to html; no report"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("Element innerHTML|abc")); + expect_throws(_ => { document.getElementById("div").innerHTML = "abc" }); + return p; + }, "Trusted Type violation report: sample for innerHTML assignment"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("HTMLScriptElement text|abc")); + expect_throws(_ => { document.getElementById("script").text = "abc" }); + return p; + }, "Trusted Type violation report: sample for text assignment"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("HTMLScriptElement src")); + expect_throws(_ => { document.getElementById("script").src = "" }); + return p; + }, "Trusted Type violation report: sample for script.src assignment"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("HTMLElement innerText|2+2;")); + expect_throws(_ => document.getElementById("script").innerText = "2+2;"); + return p; + }, "Trusted Type violation report: sample for script innerText assignment"); + + // TODO(lyf): https://crbug.com/1066791 Following tests which related to svg + // script element cause a flaky timeout in `linux-blink-rel`, following tests + // should be added back after the bug fix. + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("SVGAnimatedString baseVal")); + expect_throws(_ => { document.getElementById("svgscript").href.baseVal = "" }); + return p; + }, "Trusted Type violation report: sample for SVGScriptElement href assignment"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("Element setAttribute")); + expect_throws(_ => { document.getElementById("svgscript").setAttribute('href', "test"); }); + return p; + }, "Trusted Type violation report: sample for SVGScriptElement href assignment by setAttribute"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("SVGScriptElement text")); + expect_throws(_ => { document.getElementById("svgscript").insertBefore(document.createTextNode("Hello"), null) }); + return p; + }, "Trusted Type violation report: sample for SVGScriptElement text assignment"); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("eval|2+2")) + .then(promise_flush()); + expect_throws(_ => eval("2+2")); + flush(); + return p; + }, "Trusted Type violation report: sample for eval"); + + promise_test(t => { + // We expect the sample string to always contain the name, and at least the + // start of the value, but it should not be excessively long. + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("HTMLElement innerText|abbb")) + .then(e => assert_less_than(e.sample.length, 150)); + const value = "a" + "b".repeat(50000); + expect_throws(_ => document.getElementById("script").innerText = value); + return p; + }, "Trusted Type violation report: large values should be handled sanely."); + + // Test reporting for Custom Elements (where supported). The report should + // refer to the DOM elements being modified, so that Custom Elements cannot + // "mask" the underlying DOM mechanism (for reporting). + if (customElements) { + class CustomScript extends HTMLScriptElement {}; + customElements.define("custom-script", CustomScript, { extends: "script" }); + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("HTMLScriptElement src|abc")); + expect_throws(_ => document.getElementById("customscript").src = "abc"); + return p; + }, "Trusted Type violation report: sample for custom element assignment"); + } + + promise_test(t => { + let p = Promise.resolve() + .then(promise_violation("require-trusted-types-for 'script'")) + .then(expect_blocked_uri("trusted-types-sink")) + .then(expect_sample("Worker constructor|")) + .then(promise_flush()); + expect_throws(_ => new Worker("blabla")); + flush(); + return p; + }, "Trusted Type violation report: Worker constructor"); + + </script> + + <!-- Some elements for the tests to act on. --> + <div id="div"></div> + <script id="script"></script> + <script id="customscript" is="custom-script" src="a"></script> + <svg><script id="svgscript"></script></svg> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-reporting.html.headers b/testing/web-platform/tests/trusted-types/trusted-types-reporting.html.headers new file mode 100644 index 0000000000..31c8e41317 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-reporting.html.headers @@ -0,0 +1,6 @@ +Content-Security-Policy: trusted-types one +Content-Security-Policy-Report-Only: trusted-types two; report-uri /content-security-policy/resources/dummy-report.php +Content-Security-Policy: object-src 'none' +Content-Security-Policy: default-src * 'unsafe-inline' +Content-Security-Policy: require-trusted-types-for 'script' + diff --git a/testing/web-platform/tests/trusted-types/trusted-types-source-file-path.html b/testing/web-platform/tests/trusted-types/trusted-types-source-file-path.html new file mode 100644 index 0000000000..6e87c25a7f --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-source-file-path.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<head> + <title> + Check the reported TrustedType violation's sourceFile. + </title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/common/get-host-info.sub.js"></script> + <meta http-equiv="Content-Security-Policy" + content="require-trusted-types-for 'script'; trusted-types id"> + </head> +<body> + +<script id="to-be-modified"></script> +<script> +let toBeModified = document.querySelector("#to-be-modified"); + +let id_policy = trustedTypes.createPolicy("id", { + createHTML: x => x, + createScriptURL: x => x, + createScript: x => x, +}); + +function futureViolation() { + return new Promise(r => addEventListener("securitypolicyviolation", r), { + once: true + }); +} + +function futureScript(url) { + return new Promise(r => { + let script = document.createElement("script"); + script.src = id_policy.createScriptURL(url); + script.onload = r; + document.body.appendChild(script); + }); +} + +promise_test(async t => { + let future_violation = futureViolation(); + assert_throws_js(TypeError, _ => { + document.getElementById("to-be-modified").innerHTML = "'test'"; + }); + let violation = await future_violation; + assert_equals(violation.sourceFile, location.href) +}, "same-document script") + +promise_test(async t => { + let script_origin = get_host_info().HTTP_ORIGIN; + let script_src = script_origin + + "/trusted-types/support/set-inner-html.js"; + let script = await futureScript(script_src); + let future_violation = futureViolation(); + assert_throws_js(TypeError, () => setInnerHtml(toBeModified, "'test'")); + let violation = await future_violation; + assert_equals(violation.sourceFile, script_src); +}, "same-origin script") + +promise_test(async t => { + let script_origin = get_host_info().HTTP_REMOTE_ORIGIN; + let script_src = script_origin + + "/trusted-types/support/set-inner-html.js"; + let script = await futureScript(script_src); + let future_violation = futureViolation(); + assert_throws_js(TypeError, () => setInnerHtml(toBeModified, "'test'")); + let violation = await future_violation; + assert_equals(violation.sourceFile, script_src); +}, "cross-origin script") + +// TODO(arthursonzogni): Check what happens with redirects. Do we report the +// request's URL or the response's URL? + +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-svg-script.html b/testing/web-platform/tests/trusted-types/trusted-types-svg-script.html new file mode 100644 index 0000000000..946f825fa3 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-svg-script.html @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<head> + <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> + <div id="log"></div> + <svg id="svg"><script id="script">"some script text";</script></svg> + <script> + // Returns a promise that resolves with a Security Policy Violation (spv) + // even when it is received. + function promise_spv() { + return new Promise((resolve, reject) => { + window.addEventListener("securitypolicyviolation", e => { + resolve(e); + }, { once: true }); + }); + } + + const policy = trustedTypes.createPolicy("policy", { + createScript: x => x, createHTML: x => x, createScriptURL: x => x }); + + promise_test(t => { + assert_throws_js(TypeError, _ => { + document.getElementById("script").innerHTML = "'modified via innerHTML';"; + }); + return promise_spv(); + }, "Assign String to SVGScriptElement.innerHTML."); + + promise_test(t => { + document.getElementById("script").innerHTML = policy.createHTML("'modified via innerHTML';"); + return Promise.resolve(); + }, "Assign TrustedHTML to SVGScriptElement.innerHTML."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + elem.innerHTML = policy.createHTML("'modified via innerHTML';"); + document.getElementById("svg").appendChild(elem); + return promise_spv(); + }, "Assign TrustedHTML to SVGScriptElement.innerHTML and execute it."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + elem.insertBefore(document.createTextNode("modified via DOM"), null); + document.getElementById("svg").appendChild(elem); + return promise_spv(); + }, "Modify SVGScriptElement via DOM manipulation."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + assert_throws_js(TypeError, _ => { + elem.href.baseVal = "about:blank"; + }); + document.getElementById("svg").appendChild(elem); + return promise_spv(); + }, "Assign string to SVGScriptElement.href.baseVal."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + elem.href.baseVal = policy.createScriptURL("about:blank"); + document.getElementById("svg").appendChild(elem); + return Promise.resolve(); + }, "Assign TrustedScriptURL to SVGScriptElement.href.baseVal."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + assert_throws_js(TypeError, _ => { + elem.setAttribute("href", "about:blank"); + }); + document.getElementById("svg").appendChild(elem); + return promise_spv(); + }, "Assign string to non-attached SVGScriptElement.href via setAttribute."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + elem.setAttribute("href", policy.createScriptURL("about:blank")); + document.getElementById("svg").appendChild(elem); + return Promise.resolve(); + }, "Assign TrustedScriptURL to non-attached SVGScriptElement.href via setAttribute."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + document.getElementById("svg").appendChild(elem); + assert_throws_js(TypeError, _ => { + elem.setAttribute("href", "about:blank"); + }); + return promise_spv(); + }, "Assign string to attached SVGScriptElement.href via setAttribute."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + document.getElementById("svg").appendChild(elem); + elem.setAttribute("href", policy.createScriptURL("about:blank")); + return Promise.resolve(); + }, "Assign TrustedScriptURL to attached SVGScriptElement.href via setAttribute."); + + // Default policy test: We repate the string assignment tests above, + // but now expect all of them to pass. + promise_test(t => { + trustedTypes.createPolicy("default", { + createScript: x => x, createHTML: x => x, createScriptURL: x => x }); + return Promise.resolve(); + }, "Setup default policy"); + + promise_test(t => { + document.getElementById("script").innerHTML = "'modified via innerHTML';"; + return Promise.resolve(); + }, "Assign String to SVGScriptElement.innerHTML w/ default policy."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + elem.href.baseVal = "about:blank"; + document.getElementById("svg").appendChild(elem); + return Promise.resolve(); + }, "Assign string to SVGScriptElement.href.baseVal w/ default policy."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + elem.setAttribute("href", "about:blank"); + document.getElementById("svg").appendChild(elem); + return Promise.resolve(); + }, "Assign string to non-attached SVGScriptElement.href via setAttribute w/ default policy."); + + promise_test(t => { + const elem = document.createElementNS( + "http://www.w3.org/2000/svg", "script"); + document.getElementById("svg").appendChild(elem); + elem.setAttribute("href", "about:blank"); + return Promise.resolve(); + }, "Assign string to attached SVGScriptElement.href via setAttribute w/ default policy."); + </script> +</body> diff --git a/testing/web-platform/tests/trusted-types/trusted-types-tojson.html b/testing/web-platform/tests/trusted-types/trusted-types-tojson.html new file mode 100644 index 0000000000..72c5383099 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/trusted-types-tojson.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <meta http-equiv="Content-Security-Policy" content="trusted-types foo"> +</head> +<body> +<script> +test(t => { + let policy = trustedTypes.createPolicy("foo", { + createHTML: h => h, + createScript: s => s, + createScriptURL: u => u, + }); + + assert_equals(JSON.stringify({"x": policy.createHTML("<p>foo</p>")}), + "{\"x\":\"<p>foo</p>\"}"); + assert_equals(JSON.stringify({"x": policy.createScript("foo(bar)")}), + "{\"x\":\"foo(bar)\"}"); + assert_equals(JSON.stringify({"x": policy.createScriptURL("https://foo.example.com/bar")}), + "{\"x\":\"https://foo.example.com/bar\"}"); +}, "toJSON"); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/tt-block-eval.html b/testing/web-platform/tests/trusted-types/tt-block-eval.html new file mode 100644 index 0000000000..13178bd44b --- /dev/null +++ b/testing/web-platform/tests/trusted-types/tt-block-eval.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> + <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> + trustedTypes.createPolicy("default", {createScript: _ => null}); + + test(t => { + let a = 0; + assert_throws_js(EvalError, _ => { + eval('a="hello there"'); + }); + assert_equals(a, 0); + }, "eval blocks if the default policy rejects a value."); +</script> diff --git a/testing/web-platform/tests/trusted-types/worker-constructor.https.html b/testing/web-platform/tests/trusted-types/worker-constructor.https.html new file mode 100644 index 0000000000..6e127b11a5 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/worker-constructor.https.html @@ -0,0 +1,86 @@ +<!doctype html> +<html> +<head> + <meta http-equiv="Content-Security-Policy" content="require-trusted-types-for 'script';"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> + +const test_url = "support/WorkerGlobalScope-importScripts.https.js" +const trusted_url = trustedTypes.createPolicy("anythinggoes", { + createScriptURL: x => x}).createScriptURL(test_url); +const default_url = "support/WorkerGlobalScope-importScripts.potato.js" + +async function service_worker(url) { + if (!('serviceWorker' in navigator)) return Promise.resolve(); + + const scope = 'support/some/scope/for/this/test'; + const reg = await navigator.serviceWorker.getRegistration(scope); + if (reg) await reg.unregister(); + return await navigator.serviceWorker.register(url, {scope}); +} + +// Most tests below don't need promises, but the ones related to +// ServiceWorkers do. Since we can't mix promise and non-promise tests, +// we'll just run the non-promise tests in the main function and return +// an empty-resolved promise for those. +// Since an active default policy will affect all subsequent DOM operations, +// we're wrapping policy creation in a promise_test. Together, this will +// force proper serialization of all tests. +// +// Generally, we don't actually care what the workers here do, we'll merely +// check whether creation succeeds. + +promise_test(t => { + new Worker(trusted_url); + return Promise.resolve(); +}, "Create Worker via ScriptTestUrl"); + +promise_test(t => { + new SharedWorker(trusted_url); + return Promise.resolve(); +}, "Create SharedWorker via ScriptTestUrl"); + +promise_test(t => { + return service_worker(trusted_url); +}, "Create ServiceWorker via ScriptTestUrl"); + +promise_test(t => { + assert_throws_js(TypeError, () => new Worker(test_url)); + return Promise.resolve(); +}, "Block Worker creation via string"); + +promise_test(t => { + assert_throws_js(TypeError, () => new SharedWorker(test_url)); + return Promise.resolve(); +}, "Block SharedWorker creation via string"); + +promise_test(t => { + return promise_rejects_js(t, TypeError, service_worker(test_url)); +}, "Block ServiceWorker creation via String"); + +// Tests with default policy. +promise_test(t => { + trustedTypes.createPolicy("default", { + createScriptURL: s => s.replace("potato", "https") }); + return Promise.resolve(); +}, "Setup default policy."); + +promise_test(t => { + new Worker(default_url); + return Promise.resolve(); +}, "Create Worker via string with default policy."); + +promise_test(t => { + new SharedWorker(default_url); + return Promise.resolve(); +}, "Create SharedWorker via string with default policy."); + +promise_test(t => { + return service_worker(default_url); +}, "Create ServiceWorker via string with default policy."); + +</script> +</body> |