diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/html/browsers/sandboxing | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/browsers/sandboxing')
31 files changed, 965 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html b/testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html new file mode 100644 index 0000000000..229f6b3d85 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/inner-iframe.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + <head> + <script> + window.onload = function() { + top.calledFromIframe(); + } + </script> + </head> + <body> + <div id="inner">foo</div> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html b/testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html new file mode 100644 index 0000000000..677b5fc83a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/noscript-iframe.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<noscript>PASS</noscript> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js b/testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js new file mode 100644 index 0000000000..1ae4fad0cb --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/popup-from-initial-empty-sandboxed-document.window.js @@ -0,0 +1,46 @@ +// META: timeout=long +// META: script=/common/utils.js +// META: script=/common/dispatcher/dispatcher.js + +// Regression test for: https://crbug.com/1256822. +// +// From a sandboxed iframe allowing popups, scripts, and same-origin. Open a +// popup using the WindowProxy of a new iframe that is still on the initial +// empty document. Check that the sandbox flags are properly inherited. + +// Return true if the execution context is sandboxed. +const isSandboxed = () => { + try { + // Setting document.domain in sandboxed document throw errors. + document.domain = document.domain; + return false; + } catch (error) { + return true; + } +} + +promise_test(async test => { + // 1. Create a sandboxed iframe, allowing popups, same-origin and scripts. + const iframe_token = token(); + const iframe_document = new RemoteContext(iframe_token); + const iframe_url = remoteExecutorUrl(iframe_token); + const iframe = document.createElement("iframe"); + iframe.sandbox = "allow-same-origin allow-scripts allow-popups"; + iframe.src = iframe_url; + document.body.appendChild(iframe); + assert_true(await iframe_document.execute_script(isSandboxed), + "iframe is sandboxed"); + + // 2. From the sandboxed iframe, create an empty iframe, and open a popup + // using it's WindowProxy. The popup must inherit sandbox flags. + const popup_token = token(); + const popup_document = new RemoteContext(popup_token); + const popup_url = remoteExecutorUrl(popup_token); + iframe_document.execute_script((popup_url) => { + let iframe = document.createElement("iframe"); + iframe.name = "iframe_name"; + document.body.appendChild(iframe); + iframe_name.open(popup_url); + }, [popup_url.href]); + assert_true(await popup_document.execute_script(isSandboxed), "popup is sandboxed"); +}); diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html new file mode 100644 index 0000000000..0dc95315f1 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/check-sandbox-flags.html @@ -0,0 +1,8 @@ +<script> + try { + document.domain = document.domain; + parent.postMessage('document-domain-is-allowed', '*'); + } catch (error) { + parent.postMessage('document-domain-is-disallowed', '*'); + } +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html new file mode 100644 index 0000000000..136c494d5a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/document-open.html @@ -0,0 +1,14 @@ +<script> + onload = () => { + document.write(` + <script> + try { + document.domain = document.domain; + parent.postMessage('document-domain-is-allowed', '*'); + } catch (error) { + parent.postMessage('document-domain-is-disallowed', '*'); + } + </sc`+`ript> + `); + } +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html new file mode 100644 index 0000000000..89bd268f9c --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/execute-postmessage.html @@ -0,0 +1,5 @@ +<script> + // Execute arbitrary code from somewhere else, via postMessage. + window.addEventListener("message", event => eval(event.data)); + window.opener.postMessage("ready", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html new file mode 100644 index 0000000000..b47f0f274e --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/post-done-to-opener.html @@ -0,0 +1,3 @@ +<script> + window.opener.top.postMessage("DONE", "*"); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html new file mode 100644 index 0000000000..29c7f12441 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html @@ -0,0 +1,15 @@ +<script> + const iframe_1_script = encodeURI(` + <script> + try { + document.domain = document.domain; + parent.postMessage("not sandboxed", "*"); + } catch (exception) { + parent.postMessage("sandboxed", "*"); + } + </scr`+`ipt> + `); + + const iframe_1 = parent.document.querySelector("#iframe_1"); + iframe_1.src = `data:text/html,${iframe_1_script}`; +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers new file mode 100644 index 0000000000..82e8023d0b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-inherited-from-initiator-response-helper.html.headers @@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-scripts allow-same-origin diff --git a/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html new file mode 100644 index 0000000000..909956a54f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/resources/sandbox-javascript-window-open.html @@ -0,0 +1,18 @@ +<script> + // Forward message from the openee toward the parent. + window.addEventListener("message", event => top.postMessage(event.data, "*")); + + let check_sandboxed = `" + <script> + try { + document.domain = document.domain; + opener.postMessage('allow-document-domain', '*'); + } catch (error) { + opener.postMessage('disallow-document-domain', '*'); + } + </scr`+`ipt> + "`; + + window.open('about:blank', "window_name"); + window.open("javascript:" + check_sandboxed, "window_name"); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html new file mode 100644 index 0000000000..d6b3b099f2 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-same-origin.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <title>DOM access in sandbox="allow-same-origin" iframe</title> + <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org"> + <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <h1>DOM access in sandbox="allow-same-origin" iframe</h1> + <script type="text/javascript"> + var t = async_test("DOM access in sandbox='allow-same-origin' iframe is allowed") + var called = 0; + function calledFromIframe() { + called++; + } + function loaded() { + assert_equals(document.getElementById('sandboxedframe').contentWindow.document.getElementById('inner').innerHTML, 'foo'); + assert_equals(called, 0); + t.done(); + } + </script> + + <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox="allow-same-origin" id="sandboxedframe" onload="loaded();"></iframe> + + <div id="log"></div> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html new file mode 100644 index 0000000000..6cf3f5a4a8 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-allow-scripts.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> + <head> + <title>Script execution in sandbox="allow-scripts" iframe</title> + <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org"> + <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <h1>Script execution in sandbox="allow-scripts" iframe</h1> + <script type="text/javascript"> + var t = async_test("Running script from sandbox='allow-scripts' iframe is allowed") + var called = 0; + function calledFromIframe() { + called++; + } + function loaded() { + assert_equals(called, 1); + t.done(); + } + </script> + + <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox="allow-scripts allow-same-origin" id="sandboxedframe" onload="loaded();"></iframe> + + <div id="log"></div> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html new file mode 100644 index 0000000000..8e4b34eb8b --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-popups.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>window.open in sandbox iframe</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<body> +<script> +setup({single_test: true}); +// check that the popup's URL is not loaded +const uuid = token(); +async function assert_popup_not_loaded() { + const response = await fetch(`/fetch/api/resources/stash-take.py?key=${uuid}`); + assert_equals(await response.json(), null); // is "loaded" if it loads +} + +// check for message from the iframe +window.onmessage = e => { + assert_equals(e.data, 'null', 'return value of window.open (stringified)'); + step_timeout(async () => { + await assert_popup_not_loaded(); + done(); + }, 1000); +}; +const iframe = document.createElement('iframe'); +iframe.sandbox = 'allow-scripts'; +iframe.srcdoc = ` + <script> + let result; + try { + result = window.open('/fetch/api/resources/stash-put.py?key=${uuid}&value=loaded', '_blank'); + } catch(ex) { + result = ex; + } + parent.postMessage(String(result), '*'); + <\/script> +`; +document.body.appendChild(iframe); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html new file mode 100644 index 0000000000..0dae0137ac --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-same-origin.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> + <head> + <title>Access to sandbox iframe</title> + <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org"> + <link rel="help" href="https://html.spec.whatwg.org/multipage/#sandboxed-origin-browsing-context-flag"> + <link rel="help" href="https://html.spec.whatwg.org/multipage/#integration-with-idl"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <h1>Access to sandbox iframe</h1> + <script type="text/javascript"> + var t = async_test("Access to sandbox iframe is disallowed") + var called = 0; + function calledFromIframe() { + called++; + } + function loaded() { + t.step(() => { + assert_throws_dom("SecurityError", () => { + document.getElementById('sandboxedframe').contentWindow.document; + }); + assert_equals(called, 0); + t.done(); + }); + } + </script> + + <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox id="sandboxedframe" onload="loaded();"></iframe> + </body> + + <div id="log"></div> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html new file mode 100644 index 0000000000..3c8c0b346a --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts-via-unsandboxed-popup.tentative.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> + <script> + async_test(t => { + let i = document.createElement('iframe'); + i.sandbox = "allow-same-origin allow-popups allow-popups-to-escape-sandbox"; + i.srcdoc = `<a target='_blank' rel='opener' + href="javascript:window.opener.top.postMessage('FAIL', '*');">Click me!</a> + <a target='_blank' rel='opener' + href="./resources/post-done-to-opener.html">Click me next!</a>`; + + i.onload = _ => { + // Since the frame is sandboxed, but allow-same-origin, we can reach into it to grab the + // anchor element to click. We'll click the `javascript:` URL first, then pop up a new + // window that posts `DONE`. + // + // TODO(mkwst): This feels like a race, but it's one that we consistently win when I'm + // running the test locally 10,000 times. Good enough!™ + i.contentDocument.body.querySelectorAll('a')[0].click(); + i.contentDocument.body.querySelectorAll('a')[1].click(); + }; + document.body.appendChild(i); + + window.addEventListener('message', t.step_func(e => { + assert_not_equals(e.data, "FAIL"); + if (e.data == "DONE") + t.done(); + })); + }, "Sandboxed => unsandboxed popup"); + </script> +</body> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html new file mode 100644 index 0000000000..1bc116ada4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-disallow-scripts.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> + <head> + <title>Script execution in sandbox iframe</title> + <link rel="author" title="Kinuko Yasuda" href="mailto:kinuko@chromium.org"> + <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <h1>Script execution in sandbox iframe</h1> + <script type="text/javascript"> + var t = async_test("Running script from sandbox iframe is disallowed") + var called = 0; + function calledFromIframe() { + called++; + } + function loaded() { + assert_equals(called, 0); + t.done(); + } + </script> + + <iframe src="/html/browsers/sandboxing/inner-iframe.html" style="visibility:hidden;display:none" sandbox id="sandboxedframe" onload="loaded();"></iframe> + + <div id="log"></div> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js new file mode 100644 index 0000000000..713ca612c5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open-mutation.window.js @@ -0,0 +1,37 @@ +// Return whether the current context is sandboxed or not. The implementation do +// not matter much, but might have to change over time depending on what side +// effect sandbox flag have. Feel free to update as needed. +const is_sandboxed = () => { + try { + document.domain = document.domain; + return "not sandboxed"; + } catch (error) { + return "sandboxed"; + } +}; + +promise_test(async test => { + const message = new Promise(r => window.addEventListener("message", r)); + + const iframe_unsandboxed = document.createElement("iframe"); + document.body.appendChild(iframe_unsandboxed); + + const iframe_sandboxed = document.createElement("iframe"); + iframe_sandboxed.sandbox = "allow-same-origin allow-scripts"; + document.body.appendChild(iframe_sandboxed); + + iframe_sandboxed.srcdoc = ` + <script> + parent.frames[0].document.write(\` + <script> + const is_sandboxed = ${is_sandboxed}; + window.parent.postMessage(is_sandboxed(), '*'); + </scr\`+\`ipt> + \`); + parent.frames[0].document.close(); + </scr`+`ipt> + `; + assert_equals((await message).data, "not sandboxed"); + +}, "Using document.open() against a document from a different window must not" + + " mutate the other window's sandbox flags"); diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html new file mode 100644 index 0000000000..3f754374ba --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-document-open.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title> + Check sandbox-flags aren't lost after using document.open(). +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +promise_test(async test => { + let message = new Promise(resolve => + window.addEventListener("message", event => resolve(event.data)) + ); + + let iframe = document.createElement("iframe"); + iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); + iframe.setAttribute("src", "./resources/document-open.html") + document.body.appendChild(iframe); + + assert_equals(await message, "document-domain-is-disallowed"); +}, "document.open()"); + +promise_test(async test => { + let iframe = document.createElement("iframe"); + iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); + iframe.setAttribute("src", "/common/blank.html"); + let loaded = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + await loaded; + + let message = new Promise(resolve => + window.addEventListener("message", event => resolve(event.data)) + ); + + iframe.contentDocument.write(` + <script> + try { + document.domain = document.domain; + parent.postMessage('document-domain-is-allowed', '*'); + } catch (error) { + parent.postMessage('document-domain-is-disallowed', '*'); + } + </sc`+`ript> + `); + + assert_equals(await message, "document-domain-is-disallowed"); +}, "other_document.open()"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html new file mode 100644 index 0000000000..ab87fce5e0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-frame.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Inherit sandbox flags from the initiator's frame</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +// Check sandbox flags are properly inherited when a document initiate a +// navigation inside another frame that it doesn't own directly. + +// This check the sandbox flags defined by the frame. See also the other test +// about sandbox flags defined by the response (e.g. CSP sandbox): +// => sandbox-inherited-from-initiators-response.html + +// Return a promise, resolving when |element| triggers |event_name| event. +let future = (element, event_name) => { + return new Promise(resolve => { + element.addEventListener(event_name, event => resolve(event)) + }); +}; + +promise_test(async test => { + const iframe_1 = document.createElement("iframe"); + const iframe_2 = document.createElement("iframe"); + + iframe_1.id = "iframe_1"; + iframe_2.id = "iframe_2"; + + const iframe_1_script = encodeURI(` + <script> + try { + document.domain = document.domain; + parent.postMessage("not sandboxed", "*"); + } catch (exception) { + parent.postMessage("sandboxed", "*"); + } + </scr`+`ipt> + `); + + const iframe_2_script = ` + <script> + const iframe_1 = parent.document.querySelector("#iframe_1"); + iframe_1.src = "data:text/html,${iframe_1_script}"; + </scr`+`ipt> + `; + + iframe_2.sandbox = "allow-scripts allow-same-origin"; + iframe_2.srcdoc = iframe_2_script; + + // Insert |iframe_1|. It will load the initial empty document, with no sandbox + // flags. + const iframe_1_load_1 = future(iframe_1, "load"); + document.body.appendChild(iframe_1); + await iframe_1_load_1; + + // Insert |iframe_2|. It will load with sandbox flags. It will make |iframe_1| + // to navigate toward a data-url, which should inherit the sandbox flags. + const iframe_1_reply = future(window, "message"); + document.body.appendChild(iframe_2); + const result = await iframe_1_reply; + + assert_equals("sandboxed", result.data); +}) +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html new file mode 100644 index 0000000000..638f1ba783 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-initiator-response.html @@ -0,0 +1,46 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Inherit sandbox flags from the initiator's response</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +// Check sandbox flags are properly inherited when a document initiate a +// navigation inside another frame that it doesn't own directly. + +// This check the sandbox flags defined by the response (e.g. CSP sandbox). See +// also the other test about sandbox flags inherited from the frame. +// => sandbox-inherited-from-initiators-frame.html + +// Return a promise, resolving when |element| triggers |event_name| event. +let future = (element, event_name) => { + return new Promise(resolve => { + element.addEventListener(event_name, event => resolve(event)) + }); +}; + +promise_test(async test => { + const iframe_1 = document.createElement("iframe"); + const iframe_2 = document.createElement("iframe"); + + iframe_1.id = "iframe_1"; + iframe_2.id = "iframe_2"; + + iframe_2.src = + "./resources/sandbox-inherited-from-initiator-response-helper.html"; + + // Insert |iframe_1|. It will load the initial empty document, with no sandbox + // flags. + const iframe_1_load_1 = future(iframe_1, "load"); + document.body.appendChild(iframe_1); + await iframe_1_load_1; + + // Insert |iframe_2|. It will load with sandbox flags. It will make |iframe_1| + // to navigate toward a data-url, which should inherit the sandbox flags. + const iframe_1_reply = future(window, "message"); + document.body.appendChild(iframe_2); + const result = await iframe_1_reply; + + assert_equals("sandboxed", result.data); +}) +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html new file mode 100644 index 0000000000..04f485cc66 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-inherited-from-required-csp.html @@ -0,0 +1,154 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Inherit sandbox from CSP embedded enforcement</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<body> +<script> +// Check sandbox flags are properly defined when its parent requires them and +// the child allows it. + +const same_origin = get_host_info().HTTP_ORIGIN; +const cross_origin = get_host_info().HTTP_REMOTE_ORIGIN; +const check_sandbox_url = + "/html/browsers/sandboxing/resources/check-sandbox-flags.html?pipe="; +const allow_csp_from_star = "|header(Allow-CSP-From,*)"; + +// Return a promise, resolving when |element| triggers |event_name| event. +const future = (element, event_name, source) => { + return new Promise(resolve => { + element.addEventListener(event_name, event => { + if (!source || source.contentWindow == event.source) + resolve(event) + }) + }); +}; + +const check_sandbox_script = ` +<script> + try { + document.domain = document.domain; + parent.postMessage("document-domain-is-allowed", "*"); + } catch (exception) { + parent.postMessage("document-domain-is-disallowed", "*"); + } +</scr`+`ipt> +`; + +const sandbox_policy = "sandbox allow-scripts allow-same-origin"; + +// Test using the modern async/await primitives are easier to read/write. +// However they run sequentially, contrary to async_test. This is the parallel +// version, to avoid timing out. +let promise_test_parallel = (promise, description) => { + async_test(test => { + promise(test) + .then(() => {test.done();}) + .catch(test.step_func(error => { throw error; })); + }, description); +}; + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.csp = sandbox_policy; + + // The <iframe> immediately hosts the initial empty document after being + // appended into the DOM. It will, as long as its 'src' isn't loaded. That's + // why a page do not load is being used. + iframe.src = "/fetch/api/resources/infinite-slow-response.py"; + document.body.appendChild(iframe); + + const iframe_reply = future(window, "message", iframe); + iframe.contentDocument.write(check_sandbox_script); + const result = await iframe_reply; + iframe.remove(); + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "initial empty document"); + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.src = "data:text/html,dummy"; + + const iframe_load_1 = future(iframe, "load"); + document.body.appendChild(iframe); + await iframe_load_1; + + const iframe_load_2 = future(iframe, "load"); + iframe.csp = sandbox_policy; + iframe.src = "about:blank"; + await iframe_load_2; + + const iframe_reply = future(window, "message", iframe); + iframe.contentDocument.write(check_sandbox_script); + const result = await iframe_reply; + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "about:blank"); + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.csp = sandbox_policy; + iframe.src = + `data:text/html,${encodeURI(check_sandbox_script)}`; + + const iframe_reply = future(window, "message", iframe); + document.body.appendChild(iframe); + const result = await iframe_reply; + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "data-url"); + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.csp = sandbox_policy; + iframe.srcdoc = check_sandbox_script; + + const iframe_reply = future(window, "message", iframe); + document.body.appendChild(iframe); + const result = await iframe_reply; + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "srcdoc"); + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.csp = sandbox_policy; + + const blob = new Blob([check_sandbox_script], { type: "text/html" }); + + iframe.src = URL.createObjectURL(blob); + + const iframe_reply = future(window, "message", iframe); + document.body.appendChild(iframe); + const result = await iframe_reply; + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "blob URL"); + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.csp = sandbox_policy; + iframe.src = same_origin + check_sandbox_url + allow_csp_from_star; + + const iframe_reply = future(window, "message", iframe); + document.body.appendChild(iframe); + const result = await iframe_reply; + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "same-origin"); + +promise_test_parallel(async test => { + const iframe = document.createElement("iframe"); + iframe.csp = sandbox_policy; + iframe.src = cross_origin + check_sandbox_url + allow_csp_from_star; + + const iframe_reply = future(window, "message", iframe); + document.body.appendChild(iframe); + const result = await iframe_reply; + + assert_equals(result.data, "document-domain-is-disallowed"); +}, "cross-origin"); + +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html new file mode 100644 index 0000000000..d1306c9703 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-initial-empty-document-toward-same-origin.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title> + Check sandbox-flags inheritance in case of javascript window reuse. +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +promise_test(async test => { + let message = new Promise(resolve => + window.addEventListener("message", event => resolve(event.data)) + ); + + // Create an initial empty document in the iframe, sandboxed. It will attempt + // to load a slow page, but won't have time. + let iframe = document.createElement("iframe"); + iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); + iframe.src = "/fetch/api/resources/infinite-slow-response.py"; + document.body.appendChild(iframe); + + // Remove sandbox flags. This should apply to documents committed from + // navigations started after this instruction. + iframe.removeAttribute("sandbox"); + iframe.src = "./resources/check-sandbox-flags.html"; + + // The window is reused, but the new sandbox flags should be used. + assert_equals(await message, "document-domain-is-allowed"); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html new file mode 100644 index 0000000000..fd21e9bb02 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-javascript-window-open.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>window.open in sandbox iframe</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<body> +<script> +promise_test(async test => { + let message = new Promise(resolve => { + window.addEventListener("message", event => resolve(event.data)); + }); + let iframe = document.createElement("iframe"); + iframe.sandbox = "allow-scripts allow-popups allow-same-origin"; + iframe.src = "./resources/sandbox-javascript-window-open.html"; + document.body.appendChild(iframe); + assert_equals(await message, "disallow-document-domain"); +}); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html new file mode 100644 index 0000000000..43726e7720 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing-iframe.tentative.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script> + let result; + try { + parent.document.getElementsByClassName('script'); + result = 'iframe not sandboxed' + } catch (e) { + result = 'iframe sandboxed(' + e.message + ')'; + } + window.onmessage = m => { + window.parent.postMessage({ + result: result + }, '*'); + }; +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html new file mode 100644 index 0000000000..686f1c0c9f --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-navigation-timing.tentative.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Sandbox Navigation Timing</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<html></html> +<script> + const sandboxUrl = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1) + 'sandbox-navigation-timing-iframe.tentative.html'; + async_test(t => { + const iframe = document.createElement('iframe'); + iframe.src = sandboxUrl; + document.body.appendChild(iframe); // Navigation starts; value of sandbox flags locked on. + // This should not affect the sandbox value used for both about:blank document + // and the final document in iframe. + iframe.sandbox = 'allow-scripts'; + const iframeAboutBlankDocument = iframe.contentDocument; + + iframe.onload = t.step_func(() => { + const iframeAboutBlankContents = iframeAboutBlankDocument.querySelectorAll('body'); + assert_equals(iframeAboutBlankContents[0].tagName, "BODY", + "about:blank document's contents should still be accessible"); + + iframe.contentWindow.postMessage("is iframe sandboxed?", "*"); + }); + window.onmessage = t.step_func_done(e => { + assert_equals(e.data.result, 'iframe not sandboxed'); + }); + }, 'setting sandbox attribute should not affect current document in iframe'); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html new file mode 100644 index 0000000000..801e78f9c0 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context-iframe.html @@ -0,0 +1,5 @@ +<body> + <script> + Object.getPrototypeOf(document).changeFromSandboxedIframe = "change from sandboxed iframe"; + </script> +</body> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html new file mode 100644 index 0000000000..dc1953aee6 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-new-execution-context.html @@ -0,0 +1,39 @@ +<!doctype html> +<html> + <head> + <title>Reuse of iframe about:blank document execution context</title> + <link rel="author" title="Dan Clark" href="mailto:daniec@microsoft.com"> + <link rel="help" href="http://www.w3.org/html/wg/drafts/html/master/browsers.html#sandboxing"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + + <body> + <h1>Reuse of iframe about:blank document execution context in sandbox="allow-scripts" iframe</h1> + <script type="text/javascript"> + async_test(t => { + let iframe = document.createElement("iframe"); + document.body.appendChild(iframe); + + let iframeAboutBlankDocument = iframe.contentDocument; + assert_equals(iframeAboutBlankDocument.URL, "about:blank"); + + iframe.sandbox = "allow-scripts"; + iframe.src = './sandbox-new-execution-context-iframe.html'; + + iframe.onload = t.step_func_done(() => { + assert_equals(iframe.contentDocument, null, + "New document in sandboxed iframe should have opaque origin"); + + assert_equals(Object.getPrototypeOf(iframeAboutBlankDocument).changeFromSandboxedIframe, undefined, + "Sandboxed iframe contents should not have been able to mess with type system of about:blank document"); + + let iframeAboutBlankContents = iframeAboutBlankDocument.querySelectorAll('body'); + assert_equals(iframeAboutBlankContents[0].tagName, "BODY", + "about:blank document's contents should still be accessible"); + }); + },"iframe with sandbox should load with new execution context"); + </script> + <div id="log"></div> + </body> +</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html new file mode 100644 index 0000000000..9cf92768f7 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript-ref.html @@ -0,0 +1,6 @@ +<!doctype html> +<meta charset=utf-8> +<title>noscript parsing when sandbox disables scripting</title> +<iframe srcdoc="PASS" sandbox></iframe> +<iframe srcdoc="PASS" sandbox></iframe> +<iframe srcdoc="P<b>AS</b>S" sandbox></iframe> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html new file mode 100644 index 0000000000..bb7ced0a14 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-parse-noscript.html @@ -0,0 +1,7 @@ +<!doctype html> +<meta charset=utf-8> +<title>noscript parsing when sandbox disables scripting</title> +<link rel=match href=/html/browsers/sandboxing/sandbox-parse-noscript-ref.html> +<iframe srcdoc="<noscript>PASS</noscript>" sandbox></iframe> +<iframe src="noscript-iframe.html" sandbox></iframe> +<iframe srcdoc="<noscript>P<b>AS</b>S</noscript>" sandbox></iframe> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html new file mode 100644 index 0000000000..6fbff6df82 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/sandbox-window-open-srcdoc.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>window.open("about:srcdoc") from a sandboxed iframe</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> +// Check what happens when executing window.open("about:srcdoc") from a +// sandboxed iframe. Srcdoc can't be loaded in the main frame. It should +// result in an error page. The error page should be cross-origin with the +// opener. +// +// This test covers an interesting edge case. A main frame should inherit +// sandbox flags. However the document loaded is an internal error page. This +// might trigger some assertions, especially if the implementation wrongly +// applies the sandbox flags of the opener to the internal error page document. +// +// This test is mainly a coverage test. It passes if it doesn't crash. +async_test(test => { + let iframe = document.createElement("iframe"); + iframe.sandbox = "allow-scripts allow-popups allow-same-origin"; + iframe.srcdoc = ` + <script> + let w = window.open(); + onunload = () => w.close(); + + let notify = () => { + try { + w.origin; // Will fail after navigating to about:srcdoc. + parent.postMessage("pending", "*"); + } catch (e) { + parent.postMessage("done", "*"); + }; + }; + + addEventListener("message", notify); + notify(); + + w.location = "about:srcdoc"; // Error page. + </scr`+`ipt> + `; + + let closed = false; + addEventListener("message", event => { + closed = (event.data === "done"); + iframe.contentWindow.postMessage("ping","*"); + }); + + document.body.appendChild(iframe); + test.step_wait_func_done(()=>closed); +}, "window.open('about:srcdoc') from sandboxed srcdoc doesn't crash."); +</script> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html b/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html new file mode 100644 index 0000000000..91817c3db4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html @@ -0,0 +1,90 @@ +<meta name="timeout" content="long"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script> + +// This is a regression test for https://crbug.com/1170038. +// +// A document creates a popup and makes it navigate elsewhere. The navigation +// will never commit. The popup has not completed any real load outside of the +// initial empty document. Then from a different window with a different CSP +// policy, make it navigate to about:blank. +// +// Web browser behavior might change depending on whether a pending navigation +// exists for in the popup or not. Both are tested here. + +const same_origin = get_host_info().HTTP_ORIGIN; + +// Return a promise, resolving when |element| triggers |event_name| event. +const future = (element, event_name) => { + return new Promise(resolve => element.addEventListener(event_name, resolve)); +}; + +// `createNewPopup` is a function returning a new window that has not committed +// any real load in its frame, outside of the initial empty document. The two +// tests below vary depending on whether there is a pending navigation in the +// frame or not. +const runTest = (description, createNewPopup) => { + promise_test(async test => { + // Open a same-origin window with a different CSP. + const executor_path = + "/html/browsers/sandboxing/resources/execute-postmessage.html?pipe="; + const csp = "|header(Content-Security-Policy, " + + "sandbox" + + " allow-scripts" + + " allow-popups" + + " allow-same-origin" + + " allow-popups-to-escape-sandbox"; + const executor = window.open(same_origin + executor_path + csp); + const executor_reply = await future(window, "message"); + assert_equals(executor_reply.data, "ready"); + + const popup_name = token(); + const popup = await createNewPopup(test, popup_name); + + // Request the first real load from a DIFFERENT window with different CSPs. + const first_real_load = future(popup, "load"); + executor.postMessage(`window.open("about:blank", "${popup_name}");`); + await first_real_load; + + // The new blank document in the popup must inherit CSPs from |executor|: + let is_sandboxed = future(window, "message"); + popup.document.write(` + <script> + try { + document.domain = document.domain; + opener.opener.postMessage("not sandboxed", "*"); + } catch (error) { + opener.opener.postMessage("sandboxed", "*"); + } + </scr`+`ipt> + `); + assert_equals((await is_sandboxed).data, "sandboxed"); + }, description); +} + +// Open a new window and start loading from an unresponsive server. The frame +// will be left with the initial empty document and a pending navigation. +runTest("One pending navigation", async (test, popup_name) => { + const unresponsive_path = + "/fetch/api/resources/infinite-slow-response.py"; + return window.open(same_origin + unresponsive_path, popup_name); +}); + +// Open a new window and start loading. The response is a 204 and the navigation +// is canceled. As a result, the frame will be left with the initial empty +// document and NO pending navigation. +runTest("No pending navigation", async (test, popup_name) => { + const no_content_path = "/common/blank.html?pipe=status(204)" + const popup = window.open(same_origin + no_content_path, popup_name); + + // Unfortunately, there are no web API to detect a navigation has been + // canceled. Waiting using setTimeout is the only possible way to wait for it. + await new Promise(r => test.step_timeout(r, 1000)); + + return popup; +}); + +</script> |