summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/content-security-policy/embedded-enforcement
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/content-security-policy/embedded-enforcement')
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/META.yml1
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/allow_csp_from-header.html94
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/blocked-iframe-are-cross-origin.html59
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/change-csp-attribute-and-history-navigation.html93
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/idlharness.window.js16
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/iframe-csp-attribute.html35
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/required-csp-header-cascade.html67
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header-crlf.html87
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header.html119
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-general.html96
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-hashes.html80
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-hosts.html42
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-paths.html58
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-ports.html82
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-protocols.html66
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-nonces.html59
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-none.html113
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-self.html49
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-source_list-wildcards.html125
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-strict_dynamic.html72
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_eval.html54
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_hashes.html54
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_inline.html103
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-allow-csp-from.py43
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-policy-multiple.py25
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-required-csp.py47
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/support/embed-img-and-message-top.html14
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/support/executor.html3
-rw-r--r--testing/web-platform/tests/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js170
29 files changed, 1926 insertions, 0 deletions
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/META.yml b/testing/web-platform/tests/content-security-policy/embedded-enforcement/META.yml
new file mode 100644
index 0000000000..1cdc709f21
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/META.yml
@@ -0,0 +1 @@
+spec: https://w3c.github.io/webappsec-cspee/
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/allow_csp_from-header.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/allow_csp_from-header.html
new file mode 100644
index 0000000000..dd66bb77ac
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/allow_csp_from-header.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Allow-CSP-From header.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Same origin iframes are always allowed.",
+ "origin": Host.SAME_ORIGIN,
+ "csp": "style-src 'unsafe-inline'; script-src 'unsafe-inline'",
+ "allow_csp_from": "¢¥§",
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ { "name": "Same origin iframes are allowed even if the Allow-CSP-From is empty.",
+ "origin": Host.SAME_ORIGIN,
+ "csp": "style-src 'unsafe-inline'; script-src 'unsafe-inline'",
+ "allow_csp_from": "",
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ { "name": "Same origin iframes are allowed even if the Allow-CSP-From is not present.",
+ "origin": Host.SAME_ORIGIN,
+ "csp": "style-src 'unsafe-inline'; script-src 'unsafe-inline'",
+ "allow_csp_from": null,
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ { "name": "Same origin iframes are allowed even if Allow-CSP-From does not match origin.",
+ "origin": Host.SAME_ORIGIN,
+ "csp": "style-src 'unsafe-inline'; script-src 'unsafe-inline'",
+ "allow_csp_from": "http://example.com:888",
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ { "name": "Cross origin iframe with an empty Allow-CSP-From header gets blocked.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "script-src 'unsafe-inline'",
+ "allow_csp_from": "",
+ "expected": IframeLoad.EXPECT_BLOCK,
+ "blockedURI": null},
+ { "name": "Cross origin iframe without Allow-CSP-From header gets blocked.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "script-src 'unsafe-inline'",
+ "allow_csp_from": null,
+ "expected": IframeLoad.EXPECT_BLOCK,
+ "blockedURI": null},
+ { "name": "Cross origin iframe with correct Allow-CSP-From header is allowed.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "style-src 'unsafe-inline'; script-src 'unsafe-inline'",
+ "allow_csp_from": getOrigin(),
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ { "name": "Iframe with improper Allow-CSP-From header gets blocked.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "script-src 'unsafe-inline'",
+ "allow_csp_from": "* ¢¥§",
+ "expected": IframeLoad.EXPECT_BLOCK,
+ "blockedURI": null},
+ { "name": "Allow-CSP-From header with a star value allows cross origin frame.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "script-src 'unsafe-inline'",
+ "allow_csp_from": "*",
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ { "name": "Star Allow-CSP-From header enforces EmbeddingCSP.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "script-src 'nonce-123'",
+ "allow_csp_from": "*",
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": "inline"},
+ { "name": "Allow-CSP-From header enforces EmbeddingCSP.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "style-src 'none'; script-src 'nonce-123'",
+ "allow_csp_from": getOrigin(),
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": "inline"},
+ { "name": "'self' in blanket enforced EmbeddingCSP matches the target response origin.",
+ "origin": Host.CROSS_ORIGIN,
+ "csp": "img-src 'self'",
+ "allow_csp_from": "*",
+ "expected": IframeLoad.EXPECT_LOAD,
+ "blockedURI": null},
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithAllowCSPFrom(test.origin, test.allow_csp_from);
+ assert_iframe_with_csp(t, url, test.csp, test.expected, test.name, test.blockedURI);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/blocked-iframe-are-cross-origin.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/blocked-iframe-are-cross-origin.html
new file mode 100644
index 0000000000..0095fa3624
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/blocked-iframe-are-cross-origin.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Embedded Enforcement: blocked iframes are cross-origin.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+<script>
+
+let SecurityError = 18;
+
+promise_test(async () => {
+ let iframe = document.createElement("iframe");
+ let loaded = new Promise(r => iframe.onload = r);
+ iframe.csp = "script-src 'none'";
+ iframe.src = getCrossOrigin() + "common/blank.html";
+ document.body.appendChild(iframe);
+ await loaded;
+ assert_throws_dom(SecurityError, () => iframe.contentWindow.document);
+}, "Document blocked by embedded enforcement and its parent are cross-origin");
+
+promise_test(async () => {
+ // Create an iframe that would have been same-origin with the blocked iframe
+ // if it wasn't blocked.
+ let helper_frame = document.createElement("iframe");
+ let loaded_helper = new Promise(r => helper_frame.onload = r);
+ helper_frame.src = getCrossOrigin() +
+ "content-security-policy/embedded-enforcement/support/executor.html"
+ document.body.appendChild(helper_frame);
+ await loaded_helper;
+
+ let reply = new Promise(r => window.onmessage = r);
+ helper_frame.contentWindow.postMessage(`
+ let test = function() {
+ if (parent.frames.length != 2)
+ return "Error: Wrong number of iframes";
+
+ if (parent.frames[1] != window)
+ return "Error: Wrong frame index for the second iframe";
+
+ // Try to access frames[0] from frames[1]. This must fail.
+ try {
+ parent.frames[0].contentWindow;
+ return "Error: The error page appears same-origin";
+ } catch(dom_exception) {
+ return dom_exception.code;
+ }
+ };
+ parent.postMessage(test(), '*');
+ `, '*');
+
+ assert_equals((await reply).data, SecurityError);
+}, "Two same-origin iframes must appear as cross-origin when one is blocked");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/change-csp-attribute-and-history-navigation.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/change-csp-attribute-and-history-navigation.html
new file mode 100644
index 0000000000..64b5206177
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/change-csp-attribute-and-history-navigation.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+ let message_from = (w, starts_with) => {
+ return new Promise(resolve => {
+ window.addEventListener('message', msg => {
+ if (msg.source == w) {
+ if (!starts_with ||
+ (msg.data.startsWith && msg.data.startsWith(starts_with)))
+ resolve(msg.data);
+ }
+ });
+ });
+ };
+
+ const img_url = window.origin + "/content-security-policy/support/pass.png";
+
+ const function_addImage_string = `
+ function addImage() {
+ let img = document.createElement('img');
+ img.onload = () => top.postMessage('img loaded', '*');
+ img.onerror = () => top.postMessage('img blocked', '*');
+ img.src = '${img_url}';
+ document.body.appendChild(img);
+ }
+ `;
+
+ const html_test_payload = `
+ <!doctype html>
+ <script>${function_addImage_string}</scr`+`ipt>
+ <body onpageshow="addImage();"></body>
+ `;
+ let blob_url = URL.createObjectURL(
+ new Blob([html_test_payload], { type: 'text/html' }));
+
+ // A local-scheme document is loaded in an iframe with CSPEE. Then the csp
+ // attribute is changed and the iframe is navigated away and back. Since the
+ // policies are reloaded from history, the fact that the csp attribute changed
+ // is irrelevant.
+ promise_test(async t => {
+ // Create an iframe.
+ let iframe = document.createElement('iframe');
+ iframe.csp = "img-src 'none'; style-src 'none'";
+ document.body.appendChild(iframe);
+
+ let message_1 = message_from(iframe.contentWindow, "img");
+ iframe.src = blob_url;
+ assert_equals(await message_1, "img blocked",
+ "Img should be blocked by CSP enforced via CSPEE.");
+
+ iframe.csp = "style-src 'none'";
+ let message_2 = message_from(iframe.contentWindow, "img");
+ iframe.src = "../inheritance/support/message-top-and-navigate-back.html";
+ assert_equals(await message_2, "img blocked",
+ "Img should be blocked by CSP reloaded from history.");
+
+ let message_3 = message_from(iframe.contentWindow, "img");
+ iframe.src = "about:blank";
+ iframe.src = blob_url;
+ assert_equals(await message_3, "img loaded",
+ "Img should be allowed by CSP enforced by new csp attribute.");
+
+ }, "Iframe csp attribute changed before history navigation of local scheme.");
+
+ // A network-scheme document is loaded in an iframe with CSPEE. Then the csp
+ // attribute is changed and the iframe is navigated away and back. Since the
+ // policies are calculated again, the new csp attribute should be enforced
+ // after the history navigation.
+ promise_test(async t => {
+ // Create an iframe.
+ let iframe = document.createElement('iframe');
+ iframe.csp = "img-src 'none'; style-src 'none'";
+ document.body.appendChild(iframe);
+
+ let message_1 = message_from(iframe.contentWindow, "img");
+ iframe.src = "./support/embed-img-and-message-top.html";
+ assert_equals(await message_1, "img blocked",
+ "Img should be blocked by CSP enforced via CSPEE.");
+
+ iframe.csp = "style-src 'none'";
+ let message_2 = message_from(iframe.contentWindow, "img");
+ iframe.src = "../inheritance/support/message-top-and-navigate-back.html";
+ assert_equals(await message_2, "img loaded",
+ "Img should be allowed by CSP enforced by new csp attribute.");
+
+ }, "Iframe csp attribute changed before history navigation of network scheme.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/idlharness.window.js b/testing/web-platform/tests/content-security-policy/embedded-enforcement/idlharness.window.js
new file mode 100644
index 0000000000..2845f82c95
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/idlharness.window.js
@@ -0,0 +1,16 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+// https://w3c.github.io/webappsec-csp/embedded/
+
+'use strict';
+
+idl_test(
+ ['csp-embedded-enforcement'],
+ ['html', 'dom'],
+ idl_array => {
+ idl_array.add_objects({
+ HTMLIFrameElement: ['document.createElement("iframe")'],
+ });
+ }
+);
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/iframe-csp-attribute.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/iframe-csp-attribute.html
new file mode 100644
index 0000000000..f23be1d0e9
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/iframe-csp-attribute.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <script>
+ test(t => {
+ var i = document.createElement('iframe');
+ assert_equals('', i.csp);
+ assert_true('csp' in i);
+ assert_equals('string', typeof i.csp);
+ }, "<iframe> has a 'csp' attibute which is an empty string if undefined.");
+
+ test(t => {
+ var i = document.createElement('iframe');
+ i.setAttribute('csp', 123456);
+ assert_equals('123456', i.csp);
+ }, "<iframe>'s csp attribute is always a string.");
+
+ test(t => {
+ var i = document.createElement('iframe');
+ i.csp = 'value';
+ assert_equals('value', i.getAttribute('csp'));
+ }, "<iframe>'s 'csp content attribute reflects the IDL attribute.");
+
+ test(t => {
+ var i = document.createElement('iframe');
+ i.setAttribute('csp', 'value');
+ assert_equals('value', i.csp);
+ }, "<iframe>'s IDL attribute reflects the DOM attribute.");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/required-csp-header-cascade.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/required-csp-header-cascade.html
new file mode 100644
index 0000000000..92fe2dd431
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/required-csp-header-cascade.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Sec-Required-CSP header.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Test same policy for both iframes",
+ "csp1": "script-src 'unsafe-inline';",
+ "csp2": "script-src 'unsafe-inline';",
+ "expected1": "script-src 'unsafe-inline';",
+ "expected2": "script-src 'unsafe-inline';"},
+ { "name": "Test more restrictive policy on second iframe",
+ "csp1": "script-src 'unsafe-inline';",
+ "csp2": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected1": "script-src 'unsafe-inline';",
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test less restrictive policy on second iframe",
+ "csp1": "script-src 'unsafe-inline'; style-src 'self';",
+ "csp2": "script-src 'unsafe-inline';",
+ "expected1": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test no policy on second iframe",
+ "csp1": "script-src 'unsafe-inline'; style-src 'self';",
+ "csp2": "",
+ "expected1": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test no policy on first iframe",
+ "csp1": "",
+ "csp2": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected1": null,
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test invalid policy on first iframe (bad directive name)",
+ "csp1": "default-src http://example.com; i//nvalid-policy-name http://example.com",
+ "csp2": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected1": null,
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test invalid policy on first iframe (report directive)",
+ "csp1": "script-src 'unsafe-inline'; report-uri resources/dummy-report.php",
+ "csp2": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected1": null,
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test invalid policy on second iframe (bad directive name)",
+ "csp1": "script-src 'unsafe-inline'; style-src 'self';",
+ "csp2": "default-src http://example.com; i//nvalid-policy-name http://example.com",
+ "expected1": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ { "name": "Test invalid policy on second iframe (report directive)",
+ "csp1": "script-src 'unsafe-inline'; style-src 'self';",
+ "csp2": "script-src 'unsafe-inline'; report-uri resources/dummy-report.php",
+ "expected1": "script-src 'unsafe-inline'; style-src 'self';",
+ "expected2": "script-src 'unsafe-inline'; style-src 'self';"},
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateURLStringWithSecondIframeParams(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP, test.csp2);
+ assert_required_csp(t, url, test.csp1, [test.expected1, test.expected2]);
+ }, "Test same origin: " + test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header-crlf.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header-crlf.html
new file mode 100644
index 0000000000..414f9b73f5
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header-crlf.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Embedded Enforcement: Sec-Required-CSP header.</title>
+ <!--
+ This test is creating and navigating several iframes. This can exceed the
+ "short" timeout". See https://crbug.com/1091896
+ -->
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ // CRLF characters
+ { "name": "\\r\\n character after directive name",
+ "csp": "style-src\r\n'unsafe-inline'",
+ "expected": null },
+ { "name": "\\r\\n character in directive value",
+ "csp": "style-src 'unsafe-inline'\r\n'unsafe-eval'",
+ "expected": null },
+ { "name": "\\n character after directive name",
+ "csp": "style-src\n'unsafe-inline'",
+ "expected": null },
+ { "name": "\\n character in directive value",
+ "csp": "style-src 'unsafe-inline'\n'unsafe-eval'",
+ "expected": null },
+ { "name": "\\r character after directive name",
+ "csp": "style-src\r'unsafe-inline'",
+ "expected": null },
+ { "name": "\\r character in directive value",
+ "csp": "style-src 'unsafe-inline'\r'unsafe-eval'",
+ "expected": null },
+
+ // Attempt HTTP Header injection
+ { "name": "Attempt injecting after directive name using \\r\\n",
+ "csp": "style-src\r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive name using \\r",
+ "csp": "style-src\rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive name using \\n",
+ "csp": "style-src\nTest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after directive value using \\r\\n",
+ "csp": "style-src example.com\r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive value using \\r",
+ "csp": "style-src example.com\rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after directive value using \\n",
+ "csp": "style-src example.com\nTest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after semicolon using \\r\\n",
+ "csp": "style-src example.com;\r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after semicolon using \\r",
+ "csp": "style-src example.com;\rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after semicolon using \\n",
+ "csp": "style-src example.com;\nTest-Header-Injection: dummy",
+ "expected": null },
+
+ { "name": "Attempt injecting after space between name and value using \\r\\n",
+ "csp": "style-src \r\nTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after space between name and value using \\r",
+ "csp": "style-src \rTest-Header-Injection: dummy",
+ "expected": null },
+ { "name": "Attempt injecting after space between name and value using \\n",
+ "csp": "style-src \nTest-Header-Injection: dummy",
+ "expected": null },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateURLString(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ assert_required_csp(t, url, test.csp, [test.expected]);
+ }, "Test CRLF: " + test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header.html
new file mode 100644
index 0000000000..e0a31db8e2
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/required_csp-header.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Embedded Enforcement: Sec-Required-CSP header.</title>
+ <!--
+ This test is creating and navigating >=70 iframes. This can exceed the
+ "short" timeout". See https://crbug.com/818324
+ -->
+ <meta name="timeout" content="long">
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Sec-Required-CSP is not sent if `csp` attribute is not set on <iframe>.",
+ "csp": null,
+ "expected": null },
+ { "name": "Send Sec-Required-CSP when `csp` attribute of <iframe> is not empty.",
+ "csp": "script-src 'unsafe-inline'",
+ "expected": "script-src 'unsafe-inline'" },
+ { "name": "Send Sec-Required-CSP Header on change of `src` attribute on iframe.",
+ "csp": "script-src 'unsafe-inline'",
+ "expected": "script-src 'unsafe-inline'" },
+ { "name": "Wrong but allowed value of `csp` should still trigger sending Sec-Required-CSP Header - gibberish csp",
+ "csp": "completely wrong csp",
+ "expected": "completely wrong csp" },
+ { "name": "Wrong but allowed value of `csp` should still trigger sending Sec-Required-CSP Header - unknown policy name",
+ "csp": "invalid-policy-name http://example.com",
+ "expected": "invalid-policy-name http://example.com" },
+ { "name": "Wrong but allowed value of `csp` should still trigger sending Sec-Required-CSP Header - unknown policy name in multiple directives",
+ "csp": "media-src http://example.com; invalid-policy-name http://example.com",
+ "expected": "media-src http://example.com; invalid-policy-name http://example.com" },
+ { "name": "Wrong but allowed value of `csp` should still trigger sending Sec-Required-CSP Header - misspeled 'none'",
+ "csp": "media-src 'non'",
+ "expected": "media-src 'non'" },
+ { "name": "Wrong but allowed value of `csp` should still trigger sending Sec-Required-CSP Header - query values in path",
+ "csp": "script-src 'unsafe-inline' 127.0.0.1:8000/path?query=string",
+ "expected": "script-src 'unsafe-inline' 127.0.0.1:8000/path?query=string" },
+ { "name": "Wrong but allowed value of `csp` should still trigger sending Sec-Required-CSP Header - missing semicolon",
+ "csp": "script-src 'unsafe-inline' 'self' object-src 'self' style-src *",
+ "expected": "script-src 'unsafe-inline' 'self' object-src 'self' style-src *" },
+ { "name": "Wrong and dangerous value of `csp` should not trigger sending Sec-Required-CSP Header - comma separated",
+ "csp": "script-src 'unsafe-inline' 'self', object-src 'none'",
+ "expected": null },
+ { "name": "Wrong and dangerous value of `csp` should not trigger sending Sec-Required-CSP Header - invalid characters in directive names",
+ // script-src 127.0.0.1:8000
+ "csp": "script-src 'unsafe-inline' &#x31;&#x32;&#x37;&#x2E;&#x30;&#x2E;&#x30;&#x2E;&#x31;&#x3A;&#x38;&#x30;&#x30;&#x30;",
+ "expected": null },
+ { "name": "Wrong and dangerous value of `csp` should not trigger sending Sec-Required-CSP Header - invalid character in directive name",
+ // script-src 127.0.0.1:8000
+ "csp": "media-src%20127.0.0.1%3A8000",
+ "expected": null },
+ { "name": "Wrong and dangerous value of `csp` should not trigger sending Sec-Required-CSP Header - report-uri present",
+ "csp": "script-src 'unsafe-inline'; report-uri resources/dummy-report.php",
+ "expected": null },
+ { "name": "Wrong and dangerous value of `csp` should not trigger sending Sec-Required-CSP Header - report-to present",
+ "csp": "script-src 'unsafe-inline'; report-to resources/dummy-report.php",
+ "expected": null },
+ { "name": "Sec-Required-CSP is not sent if `csp` attribute is longer than 4096 bytes",
+ "csp": "style-src " + Array.from(Array(2044).keys()).map(i => 'a').join(' '),
+ "expected": null },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateURLString(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ assert_required_csp(t, url, test.csp, [test.expected]);
+ }, "Test same origin: " + test.name);
+
+ async_test(t => {
+ var url = generateURLString(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ var redirect_url = generateRedirect(Host.SAME_ORIGIN, url);
+ assert_required_csp(t, redirect_url, test.csp, [test.expected]);
+ }, "Test same origin redirect: " + test.name);
+
+ async_test(t => {
+ var url = generateURLString(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ var redirect_url = generateRedirect(Host.CROSS_ORIGIN, url);
+ assert_required_csp(t, redirect_url, test.csp, [test.expected]);
+ }, "Test cross origin redirect: " + test.name);
+
+ async_test(t => {
+ var url = generateURLString(Host.CROSS_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ var redirect_url = generateRedirect(Host.CROSS_ORIGIN, url);
+ assert_required_csp(t, redirect_url, test.csp, [test.expected]);
+ }, "Test cross origin redirect of cross origin iframe: " + test.name);
+
+ async_test(t => {
+ var i = document.createElement('iframe');
+ if (test.csp)
+ i.csp = test.csp;
+ i.src = generateURLString(Host.SAME_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ var loaded = false;
+
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != i.contentWindow || !('required_csp' in e.data))
+ return;
+ if (!loaded) {
+ assert_equals(e.data['required_csp'], test.expected);
+ loaded = true;
+ i.csp = "default-src 'unsafe-inline'";
+ i.src = generateURLString(Host.CROSS_ORIGIN, PolicyHeader.REQUIRED_CSP);
+ } else {
+ // Once iframe has loaded, check that on change of `src` attribute
+ // Required-CSP value is based on latest `csp` attribute value.
+ assert_equals(e.data['required_csp'], "default-src 'unsafe-inline'");
+ t.done();
+ }
+ }));
+
+ document.body.appendChild(i);
+ }, "Test Required-CSP value on `csp` change: " + test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-general.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-general.html
new file mode 100644
index 0000000000..8df4945000
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-general.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Basic implementation.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ // Note that the returned csp should always allow execution of an
+ // inline script with nonce "abc" (as returned by
+ // support/echo-policy-multiple.py), otherwise the test might
+ // return false negatives.
+ var tests = [
+ { "name": "If there is no required csp, iframe should load.",
+ "required_csp": null,
+ "returned_csp": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Iframe with empty returned CSP should be blocked.",
+ "required_csp": "style-src 'none';",
+ "returned_csp": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Iframe with matching CSP should load.",
+ "required_csp": "style-src 'none'; script-src 'unsafe-inline'",
+ "returned_csp": "style-src 'none'; script-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Iframe with more restricting CSP should load.",
+ "required_csp": "script-src 'nonce-abc' 'nonce-123'",
+ "returned_csp": "script-src 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Iframe with less restricting CSP should be blocked.",
+ "required_csp": "style-src 'none'; script-src 'none'",
+ "returned_csp": "style-src 'none'; script-src 'self' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Iframe with a different CSP should be blocked.",
+ "required_csp": "script-src 'nonce-abc' 'nonce-123'",
+ "returned_csp": "style-src 'none'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Iframe with a matching and more restrictive ports should load.",
+ "required_csp": "frame-src http://c.com:443 http://b.com",
+ "returned_csp": "frame-src http://b.com:80 http://c.com:443",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Host wildcard *.a.com does not match a.com",
+ "required_csp": "frame-src http://*.a.com",
+ "returned_csp": "frame-src http://a.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Host intersection with wildcards is computed correctly.",
+ "required_csp": "frame-sr 'none'",
+ "returned_csp": "frame-src http://a.com",
+ "returned_csp_2": "frame-src http://*.a.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Iframe should load even if the ports are different but are default for the protocols.",
+ "required_csp": "frame-src http://b.com:80",
+ "returned_csp": "child-src https://b.com:443",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Iframe should block if intersection allows sources which are not in required_csp.",
+ "required_csp": "style-src http://*.example.com:*",
+ "returned_csp": "style-src http://*.com:*",
+ "returned_csp_2": "style-src http://*.com http://*.example.com:*",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Iframe should block if intersection allows sources which are not in required_csp (other ordering).",
+ "required_csp": "style-src http://*.example.com:*",
+ "returned_csp": "style-src http://*.com:*",
+ "returned_csp_2": "style-src http://*.example.com:* http://*.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Iframe should load if intersection allows only sources which are in required_csp.",
+ "required_csp": "style-src http://*.example.com",
+ "returned_csp": "style-src http://*.example.com:*",
+ "returned_csp_2": "style-src http://*.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Removed plugin-types directive should be ignored.",
+ "required_csp": "plugin-types application/pdf",
+ "returned_csp": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Removed plugin-types directive should be ignored 2.",
+ "required_csp": "plugin-types application/pdf application/x-java-applet",
+ "returned_csp": "plugin-types application/pdf",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Removed plugin-types directive should be ignored 3.",
+ "required_csp": "style-src 'none'; plugin-types application/pdf",
+ "returned_csp": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-hashes.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-hashes.html
new file mode 100644
index 0000000000..0d8b0bc8f4
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-hashes.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Hashes.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "'sha256-abc123' is properly subsumed.",
+ "required_csp": "style-src 'sha256-abc123'",
+ "returned_csp_1": "style-src 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned should not include hashes not present in required csp.",
+ "required_csp": "style-src http://example.com",
+ "returned_csp_1": "style-src 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "'sha256-abc123' is properly subsumed with other sources.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-hashed-attributes' 'strict-dynamic' 'sha256-abc123'",
+ "returned_csp_1": "style-src http://example1.com/foo/bar.html 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Hashes do not have to be present in returned csp.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "returned_csp_1": "style-src http://example1.com/foo/",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Hashes do not have to be present in returned csp but must not allow all inline behavior.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Other expressions have to be subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-eval' 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Other expressions have to be subsumed but 'unsafe-inline' gets ignored.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline' 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Effective policy is properly found.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-hashed-attributes' 'sha256-abc123'",
+ "returned_csp_2": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required csp must allow 'sha256-abc123'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'self' 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found where 'sha256-abc123' is not subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-hashed-attributes' 'sha256-abc123'",
+ "returned_csp_2": "style-src 'sha256-abc123' 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "'sha256-abc123' is not subsumed by 'sha256-abc456'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc456'",
+ "returned_csp_1": "style-src 'unsafe-hashed-attributes' 'sha256-abc123'",
+ "returned_csp_2": "style-src 'sha256-abc123' 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy now does not allow 'sha256-abc123'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'sha256-abc456'",
+ "returned_csp_1": "style-src 'unsafe-hashed-attributes' 'sha256-abc123' 'sha256-abc456'",
+ "returned_csp_2": "style-src 'sha256-abc456' 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Effective policy is properly found where 'sha256-abc123' is not part of it.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-hashed-attributes' 'self'",
+ "returned_csp_2": "style-src 'sha256-abc123' 'self'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-hosts.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-hosts.html
new file mode 100644
index 0000000000..db3d443b83
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-hosts.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Host parts in host source expressions.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Host must match.",
+ "required_csp": "img-src http://c.com",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Hosts without wildcards must match.",
+ "required_csp": "img-src http://c.com:* http://inner.b.com",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "More specific subdomain should not match.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src http://inner.b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Specified host should not match a wildcard host.",
+ "required_csp": "img-src http://c.com:* http://inner.b.com",
+ "returned_csp": "img-src http://*.b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "A wildcard host should match a more specific host.",
+ "required_csp": "img-src http://c.com:* http://*.b.com",
+ "returned_csp": "img-src https://inner.b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-paths.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-paths.html
new file mode 100644
index 0000000000..c40b572de0
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-paths.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Path parts in host source expressions.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Returned CSP must specify a path.",
+ "required_csp": "img-src http://c.com:* http://b.com/example.html",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Returned CSP has a more specific path.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src http://b.com/example.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Matching paths.",
+ "required_csp": "img-src http://c.com:* http://b.com/example.html",
+ "returned_csp": "img-src http://b.com/example.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Empty path is not subsumed by specified paths.",
+ "required_csp": "img-src http://b.com/page1.html http://b.com/page2.html http://b.com/page3.html",
+ "returned_csp": "img-src http://b.com/",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "All specific paths match except the order.",
+ "required_csp": "img-src http://b.com/page1.html http://b.com/page2.html http://b.com/page3.html",
+ "returned_csp": "img-src http://b.com/page2.html http://b.com/page3.html http://b.com/page1.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP allows only one path.",
+ "required_csp": "img-src http://b.com/page1.html http://b.com/page2.html http://b.com/page3.html",
+ "returned_csp": "img-src http://b.com/page2.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "`/` path should be subsumed by an empty path.",
+ "required_csp": "img-src http://b.com",
+ "returned_csp": "img-src http://b.com/",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Unspecified path should be subsumed by `/`.",
+ "required_csp": "img-src http://b.com/",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "That should not be true when required csp specifies a specific page.",
+ "required_csp": "img-src http://b.com/path.html",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-ports.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-ports.html
new file mode 100644
index 0000000000..bf7ad94f6e
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-ports.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Port parts in host source expressions.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Specified ports must match.",
+ "required_csp": "img-src http://c.com:* http://b.com:80",
+ "returned_csp": "img-src http://b.com:36",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Returned CSP should be subsumed even if the port is not specified but is a default port for a scheme.",
+ "required_csp": "img-src http://c.com:* http://b.com:80",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP should be subsumed even if the port is not specified but is a default port for a more secure scheme.",
+ "required_csp": "img-src http://c.com:* http://b.com:80",
+ "returned_csp": "img-src https://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "The same should hold for `ws` case.",
+ "required_csp": "img-src http://c.com:* ws://b.com:80",
+ "returned_csp": "img-src wss://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Unspecified ports must match if schemes match.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src https://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP should be subsumed if the port is specified.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src http://b.com:80",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP should be subsumed if the port is specified but the scheme is more secure.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src https://b.com:443",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP should be subsumed if the port is specified but is not default for a more secure scheme.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src https://b.com:36",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Returned CSP should be subsumed if the ports match but schemes are not identical.",
+ "required_csp": "img-src http://c.com:* http://b.com:36",
+ "returned_csp": "img-src https://b.com:36",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP should be subsumed if the ports match but schemes are not identical for `ws`.",
+ "required_csp": "img-src http://c.com:* ws://b.com:36",
+ "returned_csp": "img-src wss://b.com:36",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Wildcard port should match unspecified port.",
+ "required_csp": "img-src http://c.com:* ws://b.com:*",
+ "returned_csp": "img-src wss://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Wildcard port should match any specific port.",
+ "required_csp": "img-src http://c.com:* ws://b.com:*",
+ "returned_csp": "img-src wss://b.com:36",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Wildcard port should match a wildcard.",
+ "required_csp": "img-src http://c.com:* ws://b.com:*",
+ "returned_csp": "img-src wss://b.com:*",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Wildcard port should not be subsumed by a default port.",
+ "required_csp": "img-src http://c.com:* ws://b.com",
+ "returned_csp": "img-src ws://b.com:*",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Wildcard port should not be subsumed by a spcified port.",
+ "required_csp": "img-src http://c.com:* ws://b.com:80",
+ "returned_csp": "img-src ws://b.com:*",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-protocols.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-protocols.html
new file mode 100644
index 0000000000..9949b8cc1a
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-host_sources-protocols.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Scheme parts in host source expressions.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "`https` is more restrictive than `http`.",
+ "required_csp": "img-src http://c.com:* https://b.com",
+ "returned_csp": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "The reverse allows iframe be to be loaded.",
+ "required_csp": "img-src http://c.com:* http://b.com",
+ "returned_csp": "img-src https://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Matching `https` protocols.",
+ "required_csp": "img-src http://c.com:* https://b.com",
+ "returned_csp": "img-src https://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "`http:` should subsume all host source expressions with this protocol.",
+ "required_csp": "img-src http:",
+ "returned_csp": "img-src http://c.com:* https://b.com http://c.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "`http:` should subsume all host source expressions with `https:`.",
+ "required_csp": "img-src http:",
+ "returned_csp": "img-src https://c.com:* https://b.com http://c.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "`http:` does not subsume other protocols.",
+ "required_csp": "img-src http:",
+ "returned_csp": "img-src https://c.com:* wss://b.com http://c.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "If scheme source is present in returned csp, it must be specified in required csp too.",
+ "required_csp": "img-src https://c.com:* wss://b.com http://c.com",
+ "returned_csp": "img-src http:",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "`http:` subsumes other `http:` source expression.",
+ "required_csp": "img-src http:",
+ "returned_csp": "img-src http: https://c.com:* https://b.com http://c.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "`http:` subsumes other `https:` source expression and expressions with `http:`.",
+ "required_csp": "img-src http:",
+ "returned_csp": "img-src https: https://c.com:* http://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "All scheme sources must be subsumed.",
+ "required_csp": "img-src http: wss:",
+ "returned_csp": "img-src https: ws:",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "All scheme sources are subsumed by their stronger variants.",
+ "required_csp": "img-src http: wss:",
+ "returned_csp": "img-src https: wss:",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-nonces.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-nonces.html
new file mode 100644
index 0000000000..33551be57d
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-nonces.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Nonces.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Exact nonce subsumes.",
+ "required_csp": "style-src 'nonce-abc'",
+ "returned_csp_1": "style-src 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Any nonce subsumes.",
+ "required_csp": "style-src 'nonce-abc'",
+ "returned_csp_1": "style-src 'nonce-xyz'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "A nonce has to be returned if required by the embedder.",
+ "required_csp": "style-src 'nonce-abc'",
+ "returned_csp_1": "style-src http://example1.com/foo",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Multiples nonces returned subsume.",
+ "required_csp": "style-src 'nonce-abc'",
+ "returned_csp_1": "style-src 'nonce-xyz' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ // nonce intersection
+ { "name": "Nonce intersection is still done on exact match - non-matching nonces.",
+ "required_csp": "style-src 'none'",
+ "returned_csp_1": "style-src 'nonce-def'",
+ "returned_csp_2": "style-src 'nonce-xyz'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Nonce intersection is still done on exact match - matching nonces.",
+ "required_csp": "style-src 'none'",
+ "returned_csp_1": "style-src 'nonce-def'",
+ "returned_csp_2": "style-src 'nonce-def' 'nonce-xyz'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ // other expressions still have to work
+ { "name": "Other expressions still have to be subsumed - positive test.",
+ "required_csp": "style-src http://example1.com/foo/ 'nonce-abc'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'nonce-xyz'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Other expressions still have to be subsumed - negative test",
+ "required_csp": "style-src http://example1.com/foo/ 'nonce-abc'",
+ "returned_csp_1": "style-src http://not-example1.com/foo/ 'nonce-xyz'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-none.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-none.html
new file mode 100644
index 0000000000..0338e067b3
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-none.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - 'none' keyword.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "Empty required csp subsumes empty list of returned policies.",
+ "required_csp": "",
+ "returned_csp_1": "",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Empty required csp subsumes any list of policies.",
+ "required_csp": "",
+ "returned_csp_1": "img-src http://example.com",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Empty required csp subsumes a policy with `none`.",
+ "required_csp": "",
+ "returned_csp_1": "img-src 'none'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required policy that allows `none` does not subsume empty list of policies.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp with effective `none` does not subsume a host source expression.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "img-src http://example.com",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp with `none` does not subsume a host source expression.",
+ "required_csp": "img-src 'none'",
+ "returned_csp_1": "img-src http://example.com",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp with effective `none` does not subsume `none` of another directive.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "frame-src 'none'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp with `none` does not subsume `none` of another directive.",
+ "required_csp": "img-src 'none'",
+ "returned_csp_1": "frame-src 'none'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp with `none` does not subsume `none` of different directives.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "img-src http://*.one.com",
+ "returned_csp_2": "frame-src https://two.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp with `none` subsumes effective list of `none`.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "img-src http://*.one.com",
+ "returned_csp_2": "img-src https://two.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required csp with `none` subsumes effective list of `none` despite other keywords.",
+ "required_csp": "img-src 'none'",
+ "returned_csp_1": "img-src http://*.one.com",
+ "returned_csp_2": "img-src 'self'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Source list with exprssions other than `none` make `none` ineffective.",
+ "required_csp": "img-src http://example.com 'none'",
+ "returned_csp_1": "img-src http://example.com",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned csp with `none` is subsumed by any required csp.",
+ "required_csp": "img-src http://example.com",
+ "returned_csp_1": "img-src 'none'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned csp with effective `none` is subsumed by any required csp.",
+ "required_csp": "img-src http://example.com",
+ "returned_csp_1": "img-src http://example.com",
+ "returned_csp_2": "img-src http://non-example.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Both required and returned csp are `none`.",
+ "required_csp": "img-src 'none'",
+ "returned_csp_1": "img-src 'none'",
+ "returned_csp_2": "img-src http://non-example.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Both required and returned csp are `none` for only one directive.",
+ "required_csp": "default-src 'none'",
+ "returned_csp_1": "img-src 'none'",
+ "returned_csp_2": "script-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Both required and returned csp are empty.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "img-src ",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Both required and returned csp are effectively 'none'.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "img-src http://a.com",
+ "returned_csp_2": "img-src http://b.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-self.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-self.html
new file mode 100644
index 0000000000..bac21cefe8
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-self.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - 'self' keyword.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "'self' keywords should match.",
+ "required_csp": "img-src 'self' http://b.com:*",
+ "returned_csp": "img-src 'self' http://b.com:*",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP does not have to specify 'self'.",
+ "required_csp": "img-src 'self' http://b.com:*",
+ "returned_csp": "img-src http://b.com:*",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned CSP must not allow 'self' if required CSP does not.",
+ "required_csp": "img-src http://b.com:*",
+ "returned_csp": "img-src 'self' http://b.com:*",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Returned 'self' should match to an origin's url.",
+ "required_csp": "img-src 'self' http://b.com:*",
+ "returned_csp": "img-src " + getCrossOrigin(),
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required 'self' should match to a origin's url.",
+ "required_csp": "img-src " + getCrossOrigin() + " http://b.com:*",
+ "returned_csp": "img-src 'self'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required 'self' should subsume a more secure version of origin's url.",
+ "required_csp": "img-src 'self' http://b.com:*",
+ "returned_csp": "img-src " + getSecureCrossOrigin(),
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned 'self' should not be subsumed by a more secure version of origin's url.",
+ "required_csp": "img-src " + getSecureCrossOrigin() + " http://b.com:*",
+ "returned_csp": "img-src 'self'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-source_list-wildcards.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-source_list-wildcards.html
new file mode 100644
index 0000000000..a2baef1d42
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-source_list-wildcards.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - Wildcard lists.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name" : "Wildcard list subsumes an empty source list.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src ",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list subsumes a source list with `none`.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src 'none'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list subsumes another wildcard list.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src *",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list subsumes a list of policies with wildcards in source lists.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src *",
+ "returned_csp_2": "img-src *",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list is equivalent to a specific list of scheme expressions and their secure variants.",
+ "required_csp": "https: http: ftp: ws: wss:",
+ "returned_csp_1": "img-src *",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list is equivalent to a specific list of scheme expressions.",
+ "required_csp": "img-src http: ftp: ws:",
+ "returned_csp_1": "img-src *",
+ "returned_csp_2": "img-src https: http: ftp: ws: wss:",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list subsumption logic should not affect other keyword expressions.",
+ "required_csp": "img-src http: ftp: ws: 'self'",
+ "returned_csp_1": "img-src *",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard list might include other scheme source expressions.",
+ "required_csp": "img-src data: blob: *",
+ "returned_csp_1": "img-src data://a.com ws://b.com ftp://c.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Effective wildcard list should be properly found.",
+ "required_csp": "img-src http://a.com ws://b.com ftp://c.com",
+ "returned_csp_1": "img-src *",
+ "returned_csp_2": "img-src http://a.com ws://b.com ftp://c.com",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name" : "Wildcard does not subsume empty list.",
+ "required_csp": "img-src *",
+ "returned_csp_1": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Empty source list does not subsume a wildcard source list.",
+ "required_csp": "img-src ",
+ "returned_csp_1": "img-src *",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "'none' does not subsume a wildcard source list.",
+ "required_csp": "img-src 'none'",
+ "returned_csp_1": "img-src *",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard source list does not subsume `data:` scheme source expression.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src data:",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard source list does not subsume `blob:` scheme source expression.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src blob:",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Source expressions do not subsume effective nonce expressions.",
+ "required_csp": "script-src http: ftp: ws:",
+ "returned_csp_1": "script-src * 'nonce-abc'",
+ "returned_csp_2": "script-src https: 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard source list is not subsumed by a host expression.",
+ "required_csp": "img-src https://another.test",
+ "returned_csp_1": "img-src *",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard list with keywords is not subsumed by a wildcard list.",
+ "required_csp": "style-src *",
+ "returned_csp_1": "style-src * 'unsafe-eval'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard list with 'unsafe-hashes' is not subsumed by a wildcard list.",
+ "required_csp": "style-src *",
+ "returned_csp_1": "style-src * 'unsafe-hashes'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard list with 'unsafe-inline' is not subsumed by a wildcard list.",
+ "required_csp": "style-src *",
+ "returned_csp_1": "style-src * 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard list with 'unsafe-eval' is not subsumed by a wildcard list.",
+ "required_csp": "img-src 'unsafe-eval'",
+ "returned_csp_1": "img-src * 'unsafe-eval'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "Wildcard list with 'unsafe-eval' is not subsumed by list with a single expression.",
+ "required_csp": "img-src 'unsafe-hashed-attributes'",
+ "returned_csp_1": "img-src * 'unsafe-hashed-attributes'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "The same as above but for 'unsafe-inline'.",
+ "required_csp": "img-src 'unsafe-inline'",
+ "returned_csp_1": "img-src * 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "`data:` is not subsumed by a wildcard list.",
+ "required_csp": "img-src *",
+ "returned_csp_1": "img-src data: blob:",
+ "returned_csp_2": "img-src data://a.com ws://b.com ftp://c.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name" : "`blob:` is not subsumed by a wildcard list.",
+ "required_csp": "img-src * data:",
+ "returned_csp_1": "img-src data: blob:",
+ "returned_csp_2": "img-src blob://a.com ws://b.com ftp://c.com",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-strict_dynamic.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-strict_dynamic.html
new file mode 100644
index 0000000000..1c35d29b71
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-strict_dynamic.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - 'strict-dynamic' keyword.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ // Note that the returned csp should always allow execution of an
+ // inline script with nonce "abc" (as returned by
+ // support/echo-policy-multiple.py), otherwise the test might
+ // return false negatives.
+ { "name": "'strict-dynamic' is ineffective for `style-src`.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'strict-dynamic' http://example1.com/foo/bar.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' is ineffective for `img-src`.",
+ "required_csp": "img-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "img-src 'strict-dynamic' http://example1.com/foo/bar.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' is ineffective for `frame-src`.",
+ "required_csp": "frame-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "frame-src 'strict-dynamic' http://example1.com/foo/bar.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' is ineffective for `child-src`.",
+ "required_csp": "child-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "child-src 'strict-dynamic' http://example1.com/foo/bar.html",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' is effective only for `script-src`.",
+ "required_csp": "script-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "script-src 'strict-dynamic' http://example1.com/foo/bar.html 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "'strict-dynamic' is properly handled for finding effective policy.",
+ "required_csp": "script-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "script-src 'strict-dynamic' http://example1.com/foo/bar.html 'nonce-abc'",
+ "returned_csp_2": "script-src 'strict-dynamic' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "'strict-dynamic' makes host source expressions ineffective.",
+ "required_csp": "script-src 'strict-dynamic' 'nonce-abc'",
+ "returned_csp_1": "script-src http://example.com 'strict-dynamic' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' makes scheme source expressions ineffective.",
+ "required_csp": "script-src 'strict-dynamic' 'nonce-abc'",
+ "returned_csp_1": "script-src http: 'strict-dynamic' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' makes 'self' ineffective.",
+ "required_csp": "script-src 'strict-dynamic' 'nonce-abc'",
+ "returned_csp_1": "script-src 'self' 'strict-dynamic' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' makes 'unsafe-inline' ineffective.",
+ "required_csp": "script-src 'strict-dynamic' 'nonce-abc'",
+ "returned_csp_1": "script-src 'unsafe-inline' 'strict-dynamic' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'strict-dynamic' has to be allowed by required csp if it is present in returned csp.",
+ "required_csp": "script-src 'nonce-abc'",
+ "returned_csp_1": "script-src 'strict-dynamic' 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_eval.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_eval.html
new file mode 100644
index 0000000000..f39fbd77c2
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_eval.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - 'unsafe-eval' keyword.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "'unsafe-eval' is properly subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-hashed-attributes' 'strict-dynamic' 'unsafe-eval'",
+ "returned_csp_1": "style-src http://example1.com/foo/bar.html 'unsafe-eval'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "No other keyword has the same effect as 'unsafe-eval'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-eval'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Other expressions have to be subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-eval'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline' 'unsafe-eval'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-eval'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-hashed-attributes' 'unsafe-eval'",
+ "returned_csp_2": "style-src http://example1.com/foo/ 'self' 'unsafe-eval'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required csp must allow 'unsafe-eval'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'self' 'unsafe-eval'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found where 'unsafe-eval' is not subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-hashed-attributes' 'unsafe-eval'",
+ "returned_csp_2": "style-src 'unsafe-eval' 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found where 'unsafe-eval' is not part of it.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-hashed-attributes' 'self'",
+ "returned_csp_2": "style-src 'unsafe-eval' 'self'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_hashes.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_hashes.html
new file mode 100644
index 0000000000..2d5fa1574a
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_hashes.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - 'unsafe-hashes' keyword.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "'unsafe-hashes' is properly subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-eval' 'strict-dynamic' 'unsafe-hashes'",
+ "returned_csp_1": "style-src http://example1.com/foo/bar.html 'unsafe-hashes'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "No other keyword has the same effect as 'unsafe-hashes'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-hashes'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Other expressions have to be subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-hashes'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline' 'unsafe-hashes'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-hashes'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-eval' 'unsafe-hashes'",
+ "returned_csp_2": "style-src http://example1.com/foo/ 'self' 'unsafe-hashes'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required csp must allow 'unsafe-hashes'.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'self' 'unsafe-hashes'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found where 'unsafe-hashes' is not subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-eval' 'unsafe-hashes'",
+ "returned_csp_2": "style-src 'unsafe-hashes' 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective policy is properly found where 'unsafe-hashes' is not part of it.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-eval' 'self'",
+ "returned_csp_2": "style-src 'unsafe-hashes' 'self'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_inline.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_inline.html
new file mode 100644
index 0000000000..4b839209c6
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/subsumption_algorithm-unsafe_inline.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Embedded Enforcement: Subsumption Algorithm - 'unsafe-inline' keyword.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testharness-helper.sub.js"></script>
+</head>
+<body>
+ <script>
+ var tests = [
+ { "name": "'strict-dynamic' is ineffective for `style-src`.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline' 'strict-dynamic'",
+ "returned_csp_1": "style-src 'unsafe-inline' http://example1.com/foo/bar.html",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' is properly subsumed in `style-src`.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' is only ineffective if the effective returned csp has nonces in `style-src`.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "style-src 'unsafe-inline' 'nonce-yay'",
+ "returned_csp_2": "style-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' is only ineffective if the effective returned csp has hashes in `style-src`.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "style-src 'unsafe-inline' 'sha256-abc123'",
+ "returned_csp_2": "style-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned csp does not have to allow 'unsafe-inline' in `style-src` to be subsumed.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "style-src 'self'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' does not matter if returned csp is effectively `none`.",
+ "required_csp": "style-src 'unsafe-inline'",
+ "returned_csp_1": "style-src ",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' is properly subsumed in `script-src`.",
+ "required_csp": "script-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "script-src http://example1.com/foo/ 'unsafe-inline'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Returned csp only loads 'unsafe-inline' scripts with 'nonce-abc'.",
+ "required_csp": "script-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "script-src 'nonce-abc'",
+ "returned_csp_2": "script-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' is ineffective when nonces are present.",
+ "required_csp": "script-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "script-src 'unsafe-inline' 'nonce-abc'",
+ "returned_csp_2": "script-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "'unsafe-inline' is only ineffective if the effective returned csp has hashes in `script-src`.",
+ "required_csp": "script-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "script-src 'unsafe-inline' 'sha256-abc123' 'nonce-abc'",
+ "returned_csp_2": "script-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ { "name": "Required csp allows `strict-dynamic`, but retuned csp does.",
+ "required_csp": "script-src http://example1.com/foo/ 'unsafe-inline' 'strict-dynamic'",
+ "returned_csp_1": "script-src 'unsafe-inline' http://example1.com/foo/bar.html",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Required csp does not allow `unsafe-inline`, but retuned csp does.",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-inline'",
+ "returned_csp_2": null,
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Returned csp allows a nonce.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "style-src 'unsafe-inline' 'nonce-abc'",
+ "returned_csp_2": "style-src 'nonce-abc'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Returned csp allows a hash.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline'",
+ "returned_csp_1": "style-src 'unsafe-inline' 'sha256-abc123'",
+ "returned_csp_2": "style-src 'sha256-abc123'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective returned csp allows 'unsafe-inline'",
+ "required_csp": "style-src http://example1.com/foo/ 'self'",
+ "returned_csp_1": "style-src 'unsafe-inline' https://example.test/",
+ "returned_csp_2": "style-src 'unsafe-inline'",
+ "expected": IframeLoad.EXPECT_BLOCK },
+ { "name": "Effective returned csp does not allow 'sha512-321cba' hash.",
+ "required_csp": "style-src http://example1.com/foo/ 'self' 'unsafe-inline' 'sha512-321cba'",
+ "returned_csp_1": "style-src http://example1.com/foo/ 'unsafe-inline' 'nonce-yay'",
+ "returned_csp_2": "style-src http://example1.com/foo/ 'unsafe-inline' 'sha512-321cba'",
+ "expected": IframeLoad.EXPECT_LOAD },
+ ];
+ tests.forEach(test => {
+ async_test(t => {
+ var url = generateUrlWithPolicies(Host.CROSS_ORIGIN, test.returned_csp_1);
+ if (test.returned_csp_2)
+ url.searchParams.append("policy2", test.returned_csp_2);
+ assert_iframe_with_csp(t, url, test.required_csp, test.expected, test.name, null);
+ }, test.name);
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-allow-csp-from.py b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-allow-csp-from.py
new file mode 100644
index 0000000000..3a91437967
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-allow-csp-from.py
@@ -0,0 +1,43 @@
+import json
+def main(request, response):
+ headers = [(b"Content-Type", b"text/html")]
+ if b"allow_csp_from" in request.GET:
+ headers.append((b"Allow-CSP-From", request.GET[b"allow_csp_from"]))
+ message = request.GET[b"id"]
+ return headers, b'''
+<!DOCTYPE html>
+<html>
+<head>
+ <title>This page enforces embedder's policies</title>
+ <script nonce="123">
+ document.addEventListener("securitypolicyviolation", function(e) {
+ var response = {};
+ response["id"] = "%s";
+ response["securitypolicyviolation"] = true;
+ response["blockedURI"] = e.blockedURI;
+ response["lineNumber"] = e.lineNumber;
+ window.top.postMessage(response, '*');
+ });
+ </script>
+</head>
+<body>
+ <script nonce="123">
+ let img = document.createElement('img');
+ img.src = "../../support/pass.png";
+ img.onload = function() { window.top.postMessage("img loaded", '*'); }
+ document.body.appendChild(img);
+ </script>
+ <style>
+ body {
+ background-color: maroon;
+ }
+ </style>
+ <script nonce="abc">
+ var response = {};
+ response["id"] = "%s";
+ response["loaded"] = true;
+ window.top.postMessage(response, '*');
+ </script>
+</body>
+</html>
+''' % (message, message)
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-policy-multiple.py b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-policy-multiple.py
new file mode 100644
index 0000000000..b91bf0d5ea
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-policy-multiple.py
@@ -0,0 +1,25 @@
+def main(request, response):
+ headers = [(b"Content-Type", b"text/html")]
+ if b"policy" in request.GET:
+ headers.append((b"Content-Security-Policy", request.GET[b"policy"]))
+ if b"policy2" in request.GET:
+ headers.append((b"Content-Security-Policy", request.GET[b"policy2"]))
+ if b"policy3" in request.GET:
+ headers.append((b"Content-Security-Policy", request.GET[b"policy3"]))
+ message = request.GET[b"id"]
+ return headers, b'''
+<!DOCTYPE html>
+<html>
+<head>
+ <title>This page sets given CSP upon itself.</title>
+</head>
+<body>
+ <script nonce="abc">
+ var response = {};
+ response["id"] = "%s";
+ response["loaded"] = true;
+ window.top.postMessage(response, '*');
+ </script>
+</body>
+</html>
+''' % (message)
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-required-csp.py b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-required-csp.py
new file mode 100644
index 0000000000..b704dfe92f
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/echo-required-csp.py
@@ -0,0 +1,47 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ message = {}
+
+ header = request.headers.get(b"Test-Header-Injection");
+ message[u'test_header_injection'] = isomorphic_decode(header) if header else None
+
+ header = request.headers.get(b"Sec-Required-CSP");
+ message[u'required_csp'] = isomorphic_decode(header) if header else None
+
+ second_level_iframe_code = u""
+ if b"include_second_level_iframe" in request.GET:
+ if b"second_level_iframe_csp" in request.GET and request.GET[b"second_level_iframe_csp"] != b"":
+ second_level_iframe_code = u'''<script>
+ var i2 = document.createElement('iframe');
+ i2.src = 'echo-required-csp.py';
+ i2.csp = "{0}";
+ document.body.appendChild(i2);
+ </script>'''.format(isomorphic_decode(request.GET[b"second_level_iframe_csp"]))
+ else:
+ second_level_iframe_code = u'''<script>
+ var i2 = document.createElement('iframe');
+ i2.src = 'echo-required-csp.py';
+ document.body.appendChild(i2);
+ </script>'''
+
+ return [(b"Content-Type", b"text/html"), (b"Allow-CSP-From", b"*")], u'''
+<!DOCTYPE html>
+<html>
+<head>
+ <!--{2}-->
+ <script>
+ window.addEventListener('message', function(e) {{
+ window.parent.postMessage(e.data, '*');
+ }});
+
+ window.parent.postMessage({0}, '*');
+ </script>
+</head>
+<body>
+{1}
+</body>
+</html>
+'''.format(json.dumps(message), second_level_iframe_code, str(request.headers))
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/embed-img-and-message-top.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/embed-img-and-message-top.html
new file mode 100644
index 0000000000..ab0e22d82f
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/embed-img-and-message-top.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ function addImage() {
+ let img = document.createElement('img');
+ img.onload = () => top.postMessage('img loaded', '*');
+ img.onerror = () => top.postMessage('img blocked', '*');
+ img.src = '/content-security-policy/support/pass.png';
+ document.body.appendChild(img);
+ }
+ </script>
+ <body onpageshow="addImage();">
+ </body>
+</html>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/executor.html b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/executor.html
new file mode 100644
index 0000000000..dc277a6ef0
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/executor.html
@@ -0,0 +1,3 @@
+<script>
+ window.onmessage = event => eval(event.data);
+</script>
diff --git a/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js
new file mode 100644
index 0000000000..4adc521696
--- /dev/null
+++ b/testing/web-platform/tests/content-security-policy/embedded-enforcement/support/testharness-helper.sub.js
@@ -0,0 +1,170 @@
+const Host = {
+ SAME_ORIGIN: "same-origin",
+ CROSS_ORIGIN: "cross-origin",
+};
+
+const PolicyHeader = {
+ CSP: "echo-policy.py?policy=",
+ CSP_MULTIPLE: "echo-policy-multiple.py",
+ REQUIRED_CSP: "echo-required-csp.py",
+ ALLOW_CSP_FROM: "echo-allow-csp-from.py",
+};
+
+const IframeLoad = {
+ EXPECT_BLOCK: true,
+ EXPECT_LOAD: false,
+};
+
+function getOrigin() {
+ var url = new URL("http://{{host}}:{{ports[http][0]}}/");
+ return url.origin;
+}
+
+function getCrossOrigin() {
+ var url = new URL("http://{{domains[天気の良い日]}}:{{ports[http][0]}}/");
+ return url.toString();
+}
+
+function getSecureCrossOrigin() {
+ // Since wptserve spins up servers on non-default port, 'self' matches
+ // http://[host]:[specified-port] and https://[host]:[specified-port], but not
+ // https://[host]:[https-port]. So, we use the http port for this https origin
+ // in order to verify that a secure variant of a non-secure URL matches 'self'.
+ var url = new URL("https://{{domains[天気の良い日]}}:{{ports[http][0]}}");
+ return url.toString();
+}
+
+function generateURL(host, path, include_second_level_iframe, second_level_iframe_csp) {
+ var url = new URL("http://{{host}}:{{ports[http][0]}}/content-security-policy/embedded-enforcement/support/");
+ url.hostname = host == Host.SAME_ORIGIN ? "{{host}}" : "{{domains[天気の良い日]}}";
+ url.pathname += path;
+ if (include_second_level_iframe) {
+ url.searchParams.append("include_second_level_iframe", "");
+ if (second_level_iframe_csp)
+ url.searchParams.append("second_level_iframe_csp", second_level_iframe_csp);
+ }
+
+ return url;
+}
+
+function generateURLString(host, path) {
+ return generateURL(host, path, false, "").toString();
+}
+
+function generateURLStringWithSecondIframeParams(host, path, second_level_iframe_csp) {
+ return generateURL(host, path, true, second_level_iframe_csp).toString();
+}
+
+function generateRedirect(host, target) {
+ var url = new URL("http://{{host}}:{{ports[http][0]}}/common/redirect.py?location=" +
+ encodeURIComponent(target));
+ url.hostname = host == Host.SAME_ORIGIN ? "{{host}}" : "{{domains[天気の良い日]}}";
+
+ return url.toString();
+}
+
+function generateUrlWithPolicies(host, policy) {
+ var url = generateURL(host, PolicyHeader.CSP_MULTIPLE);
+ if (policy != null)
+ url.searchParams.append("policy", policy);
+ return url;
+}
+
+function generateUrlWithAllowCSPFrom(host, allowCspFrom) {
+ var url = generateURL(host, PolicyHeader.ALLOW_CSP_FROM);
+ if (allowCspFrom != null)
+ url.searchParams.append("allow_csp_from", allowCspFrom);
+ return url;
+}
+
+function assert_required_csp(t, url, csp, expected) {
+ var i = document.createElement('iframe');
+ if(csp)
+ i.csp = csp;
+ i.src = url;
+
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != i.contentWindow || !('required_csp' in e.data))
+ return;
+
+ if (expected.indexOf(e.data['required_csp']) == -1)
+ assert_unreached('Child iframes have unexpected csp:"' + e.data['required_csp'] + '"');
+
+ expected.splice(expected.indexOf(e.data['required_csp']), 1);
+
+ if (e.data['test_header_injection'] != null)
+ assert_unreached('HTTP header injection was successful');
+
+ if (expected.length == 0)
+ t.done();
+ }));
+
+ document.body.appendChild(i);
+}
+
+function assert_iframe_with_csp(t, url, csp, shouldBlock, urlId, blockedURI) {
+ var i = document.createElement('iframe');
+ url.searchParams.append("id", urlId);
+ i.src = url.toString();
+ if (csp != null)
+ i.csp = csp;
+
+ var loaded = {};
+ window.addEventListener("message", function (e) {
+ if (e.source != i.contentWindow)
+ return;
+ if (e.data["loaded"])
+ loaded[e.data["id"]] = true;
+ });
+
+ if (shouldBlock) {
+ // Assert iframe does not load and is inaccessible.
+ window.onmessage = t.step_func(function(e) {
+ if (e.source != i.contentWindow)
+ return;
+ assert_unreached('No message should be sent from the frame.');
+ });
+ i.onload = t.step_func(function () {
+ // Delay the check until after the postMessage has a chance to execute.
+ setTimeout(t.step_func_done(function () {
+ assert_equals(loaded[urlId], undefined);
+ }), 500);
+ assert_throws_dom("SecurityError", () => {
+ var x = i.contentWindow.location.href;
+ });
+ });
+ } else if (blockedURI) {
+ // Assert iframe loads with an expected violation.
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != i.contentWindow)
+ return;
+ if (!e.data.securitypolicyviolation)
+ return;
+ assert_equals(e.data["blockedURI"], blockedURI);
+ t.done();
+ }));
+ } else {
+ // Assert iframe loads. Wait for the load event, the postMessage from the
+ // script and the img load event.
+ let postMessage_received = false;
+ let img_loaded = false;
+ window.addEventListener('message', t.step_func(e => {
+ if (e.source != i.contentWindow)
+ return;
+ if (e.data.loaded) {
+ assert_true(loaded[urlId]);
+ postMessage_received = true;
+ } else if (e.data === "img.loaded")
+ img_loaded = true;
+
+ if (i.onloadReceived && postMessage_received && img_loaded)
+ t.done();
+ }));
+ i.onload = t.step_func(function () {
+ if (loaded[urlId])
+ t.done();
+ i.onloadReceived = true;
+ });
+ }
+ document.body.appendChild(i);
+}