Bug 1073952: tests for iframe sandbox srcdoc and data URIs with CSP r=ckerschb,dveditz
MozReview-Commit-ID: 5Q8XIJPrRPk
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed</title>
+</head>
+<body>
+<iframe srcdoc="<img src=x onerror='parent.postMessage({result: `unexpected-csp-violation`}, `*`);'>"
+ sandbox="allow-scripts"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^
@@ -0,0 +1,1 @@
+content-security-policy: default-src *;
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_srcdoc.sjs
@@ -0,0 +1,79 @@
+// Custom *.sjs file specifically for the needs of
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1073952
+
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const SCRIPT = `
+ <script>
+ parent.parent.postMessage({result: "allowed"}, "*");
+ </script>`;
+
+const SIMPLE_IFRAME_SRCDOC = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <iframe sandbox="allow-scripts" srcdoc="` + SCRIPT + `"></iframe>
+ </body>
+ </html>`;
+
+const INNER_SRCDOC_IFRAME = `
+ <iframe sandbox='allow-scripts' srcdoc='<script>
+ parent.parent.parent.postMessage({result: "allowed"}, "*");
+ </script>'>
+ </iframe>`;
+
+const NESTED_IFRAME_SRCDOC = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <iframe sandbox="allow-scripts" srcdoc="` + INNER_SRCDOC_IFRAME + `"></iframe>
+ </body>
+ </html>`;
+
+
+const INNER_DATAURI_IFRAME = `
+ <iframe sandbox='allow-scripts' src='data:text/html,<script>
+ parent.parent.parent.postMessage({result: "allowed"}, "*");
+ </script>'>
+ </iframe>`;
+
+const NESTED_IFRAME_SRCDOC_DATAURI = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <iframe sandbox="allow-scripts" srcdoc="` + INNER_DATAURI_IFRAME + `"></iframe>
+ </body>
+ </html>`;
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ if (typeof query.get("csp") === "string") {
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+ }
+ response.setHeader("Content-Type", "text/html", false);
+
+ if (query.get("action") === "simple_iframe_srcdoc") {
+ response.write(SIMPLE_IFRAME_SRCDOC);
+ return;
+ }
+
+ if (query.get("action") === "nested_iframe_srcdoc") {
+ response.write(NESTED_IFRAME_SRCDOC);
+ return;
+ }
+
+ if (query.get("action") === "nested_iframe_srcdoc_datauri") {
+ response.write(NESTED_IFRAME_SRCDOC_DATAURI);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -200,16 +200,19 @@ support-files =
file_strict_dynamic_non_parser_inserted.html
file_strict_dynamic_non_parser_inserted_inline.html
file_strict_dynamic_unsafe_eval.html
file_strict_dynamic_default_src.html
file_strict_dynamic_default_src.js
file_upgrade_insecure_navigation.sjs
file_punycode_host_src.sjs
file_punycode_host_src.js
+ file_iframe_srcdoc.sjs
+ file_iframe_sandbox_srcdoc.html
+ file_iframe_sandbox_srcdoc.html^headers^
[test_base-uri.html]
[test_blob_data_schemes.html]
[test_connect-src.html]
[test_CSP.html]
[test_allow_https_schemes.html]
[test_bug663567.html]
[test_bug802872.html]
@@ -288,8 +291,10 @@ tags = mcb
[test_upgrade_insecure_docwrite_iframe.html]
[test_bug1242019.html]
[test_bug1312272.html]
[test_strict_dynamic.html]
[test_strict_dynamic_parser_inserted.html]
[test_strict_dynamic_default_src.html]
[test_upgrade_insecure_navigation.html]
[test_punycode_host_src.html]
+[test_iframe_sandbox_srcdoc.html]
+[test_iframe_srcdoc.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_sandbox_srcdoc.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display">Bug 1073952</p>
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+
+ if(topic === "csp-on-violate-policy") {
+ var violationString = SpecialPowers.getPrivilegedProps(SpecialPowers.
+ do_QueryInterface(subject, "nsISupportsCString"), "data");
+ // the violation subject for inline script violations is unfortunately vague,
+ // all we can do is match the string.
+ if (!violationString.includes("Inline Script")) {
+ return
+ }
+ ok(true, "CSP inherited into sandboxed srcdoc iframe, script blocked.");
+ window.testFinished();
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+window.examiner = new examiner();
+
+function testFinished() {
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+addEventListener("message", function(e) {
+ ok(false, "We should not execute JS in srcdoc iframe.");
+ window.testFinished();
+})
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_iframe_sandbox_srcdoc.html';
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_srcdoc.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1073952 - Test CSP enforcement within iframe srcdoc</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * (1) We serve a site which makes use of script-allowed sandboxed iframe srcdoc
+ * and make sure that CSP applies to the nested browsing context
+ * within the iframe.
+ * [PAGE WITH CSP [IFRAME SANDBOX SRCDOC [SCRIPT]]]
+ *
+ * (2) We serve a site which nests script within an script-allowed sandboxed
+ * iframe srcdoc within another script-allowed sandboxed iframe srcdoc and
+ * make sure that CSP applies to the nested browsing context
+ * within the iframe*s*.
+ * [PAGE WITH CSP [IFRAME SANDBOX SRCDOC [IFRAME SANDBOX SRCDOC [SCRIPT]]]]
+ *
+ * Please note that the test relies on the "csp-on-violate-policy" observer.
+ * Whenever the script within the iframe is blocked observers are notified.
+ * In turn, this renders the 'result' within tests[] unused. In case the script
+ * would execute however, the postMessageHandler would bubble up 'allowed' and
+ * the test would fail.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ // [PAGE *WITHOUT* CSP [IFRAME SRCDOC [SCRIPT]]]
+ { csp: "",
+ result: "allowed",
+ query: "simple_iframe_srcdoc",
+ desc: "No CSP should run script within script-allowed sandboxed iframe srcdoc"
+ },
+ { csp: "script-src https://test1.com",
+ result: "blocked",
+ query: "simple_iframe_srcdoc",
+ desc: "CSP should block script within script-allowed sandboxediframe srcdoc"
+ },
+ // [PAGE *WITHOUT* CSP [IFRAME SRCDOC [IFRAME SRCDOC [SCRIPT]]]]
+ { csp: "",
+ result: "allowed",
+ query: "nested_iframe_srcdoc",
+ desc: "No CSP should run script within script-allowed sandboxed iframe srcdoc nested within another script-allowed sandboxed iframe srcdoc"
+ },
+ // [PAGE WITH CSP [IFRAME SRCDOC ]]
+ { csp: "script-src https://test2.com",
+ result: "blocked",
+ query: "nested_iframe_srcdoc",
+ desc: "CSP should block script within script-allowed sandboxed iframe srcdoc nested within another script-allowed sandboxed iframe srcdoc"
+ },
+ { csp: "",
+ result: "allowed",
+ query: "nested_iframe_srcdoc_datauri",
+ desc: "No CSP, should run script within script-allowed sandboxed iframe src with data URL nested within another script-allowed sandboxed iframe srcdoc"
+ },
+ { csp: "script-src https://test3.com",
+ result: "blocked",
+ query: "nested_iframe_srcdoc_datauri",
+ desc: "CSP should block script within script-allowed sandboxed iframe src with data URL nested within another script-allowed sandboxed iframe srcdoc"
+ },
+
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ var result = event.data.result;
+ testComplete(result, tests[counter].result, tests[counter].desc);
+}
+
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "csp-on-violate-policy") {
+ var violationString = SpecialPowers.getPrivilegedProps(SpecialPowers.
+ do_QueryInterface(subject, "nsISupportsCString"), "data");
+ // the violation subject for inline script violations is unfortunately vague,
+ // all we can do is match the string.
+ if (!violationString.includes("Inline Script")) {
+ return
+ }
+ testComplete("blocked", tests[counter].result, tests[counter].desc);
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+function testComplete(result, expected, desc) {
+ is(result, expected, desc);
+ // ignore cases when we get csp violations and postMessage from the same frame.
+ var frameURL = new URL(document.getElementById("testframe").src);
+ var params = new URLSearchParams(frameURL.search);
+ var counterInFrame = params.get("counter");
+ if (counterInFrame == counter) {
+ loadNextTest();
+ }
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+ var src = "file_iframe_srcdoc.sjs";
+ src += "?csp=" + escape(tests[counter].csp);
+ src += "&action=" + escape(tests[counter].query);
+ src += "&counter=" + counter;
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+window.examiner = new examiner();
+loadNextTest();
+
+</script>
+</body>
+</html>