diff options
Diffstat (limited to 'testing/web-platform/tests/content-security-policy/embedded-enforcement')
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' 127.0.0.1:8000", + "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); +} |