diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index 1bc30c752ac5..969652703132 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -1690,8 +1690,10 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url, nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; } - // If we have the document, use it - if (doc) { + // If we have the document, use it. Unfortunately, for dedicated workers + // 'doc' ends up being the parent document, which is not the document + // that we want to use. So make sure to avoid using 'doc' in that situation. + if (doc && doc->NodePrincipal() == mPrincipal) { rv = NS_NewChannel(getter_AddRefs(mChannel), uri, doc, diff --git a/dom/security/test/csp/file_main.html^headers^ b/dom/security/test/csp/file_main.html^headers^ index c7fdbae20471..3338de389b31 100644 --- a/dom/security/test/csp/file_main.html^headers^ +++ b/dom/security/test/csp/file_main.html^headers^ @@ -1 +1 @@ -Content-Security-Policy: default-src 'self' ; style-src 'unsafe-inline' 'self' +Content-Security-Policy: default-src 'self' blob: ; style-src 'unsafe-inline' 'self' diff --git a/dom/security/test/csp/file_main.js b/dom/security/test/csp/file_main.js index 864a5d230b95..0bc15b6827a4 100644 --- a/dom/security/test/csp/file_main.js +++ b/dom/security/test/csp/file_main.js @@ -1,16 +1,28 @@ -// some javascript for the CSP XHR tests -// +function doXHR(uri) { + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", uri); + xhr.send(); + } catch(ex) {} +} +doXHR("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_good"); +doXHR("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_bad"); +fetch("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_good"); +fetch("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_bad"); +navigator.sendBeacon("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_good"); try { - var xhr_good = new XMLHttpRequest(); - var xhr_good_uri ="http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_good"; - xhr_good.open("GET", xhr_good_uri, true); - xhr_good.send(null); -} catch(e) {} + navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad"); +} catch(ex) {} -try { - var xhr_bad = new XMLHttpRequest(); - var xhr_bad_uri ="http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_bad"; - xhr_bad.open("GET", xhr_bad_uri, true); - xhr_bad.send(null); -} catch(e) {} + +new Worker("file_main_worker.js").postMessage({inherited : false}); + + +var blobxhr = new XMLHttpRequest(); +blobxhr.open("GET", "file_main_worker.js") +blobxhr.responseType = "blob"; +blobxhr.send(); +blobxhr.onload = () => { + new Worker(URL.createObjectURL(blobxhr.response)).postMessage({inherited : true}); +} diff --git a/dom/security/test/csp/file_main_worker.js b/dom/security/test/csp/file_main_worker.js new file mode 100644 index 000000000000..d8953eec7489 --- /dev/null +++ b/dom/security/test/csp/file_main_worker.js @@ -0,0 +1,28 @@ +function doXHR(uri) { + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", uri); + xhr.send(); + } catch(ex) {} +} + +var sameBase = "http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid="; +var crossBase = "http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid="; + +onmessage = (e) => { + for (base of [sameBase, crossBase]) { + var prefix; + var suffix; + if (e.data.inherited) { + prefix = base + "worker_inherited_" + suffix = base == sameBase ? "_good" : "_bad"; + } + else { + prefix = base + "worker_" + suffix = base == sameBase ? "_same_good" : "_cross_good"; + } + doXHR(prefix + "xhr" + suffix); + fetch(prefix + "fetch" + suffix); + try { importScripts(prefix + "script" + suffix); } catch(ex) {} + } +} diff --git a/dom/security/test/csp/file_redirects_main.html b/dom/security/test/csp/file_redirects_main.html index 651f44d38495..5c9affea04ba 100644 --- a/dom/security/test/csp/file_redirects_main.html +++ b/dom/security/test/csp/file_redirects_main.html @@ -11,17 +11,18 @@ var thisSite = "http://mochi.test:8888"; var otherSite = "http://example.com"; var page = "/tests/dom/security/test/csp/file_redirects_page.sjs"; -var tests = { "font-src": thisSite+page+"?testid=font-src&csp=1", - "frame-src": thisSite+page+"?testid=frame-src&csp=1", - "img-src": thisSite+page+"?testid=img-src&csp=1", - "media-src": thisSite+page+"?testid=media-src&csp=1", - "object-src": thisSite+page+"?testid=object-src&csp=1", - "script-src": thisSite+page+"?testid=script-src&csp=1", - "style-src": thisSite+page+"?testid=style-src&csp=1", - "worker": thisSite+page+"?testid=worker&csp=1", - "xhr-src": thisSite+page+"?testid=xhr-src&csp=1", - "script-src-from-worker": thisSite+page+"?testid=script-src-from-worker&csp=1", - "img-src-from-css": thisSite+page+"?testid=img-src-from-css&csp=1", +var tests = { "font-src": thisSite+page+"?testid=font-src", + "frame-src": thisSite+page+"?testid=frame-src", + "img-src": thisSite+page+"?testid=img-src", + "media-src": thisSite+page+"?testid=media-src", + "object-src": thisSite+page+"?testid=object-src", + "script-src": thisSite+page+"?testid=script-src", + "style-src": thisSite+page+"?testid=style-src", + "worker": thisSite+page+"?testid=worker", + "xhr-src": thisSite+page+"?testid=xhr-src", + "from-worker": thisSite+page+"?testid=from-worker", + "from-blob-worker": thisSite+page+"?testid=from-blob-worker", + "img-src-from-css": thisSite+page+"?testid=img-src-from-css", }; var container = document.getElementById("container"); diff --git a/dom/security/test/csp/file_redirects_page.sjs b/dom/security/test/csp/file_redirects_page.sjs index a849185d5c7f..9e3c0d0350d4 100644 --- a/dom/security/test/csp/file_redirects_page.sjs +++ b/dom/security/test/csp/file_redirects_page.sjs @@ -14,15 +14,13 @@ function handleRequest(request, response) var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs"; // CSP header value - if (query["csp"] == 1) { - var additional = "" - if (query['testid'] == "worker") { - additional = "; script-src 'self' 'unsafe-inline'"; - } - response.setHeader("Content-Security-Policy", - "default-src 'self' ; style-src 'self' 'unsafe-inline'" + additional, - false); + var additional = "" + if (query['testid'] == "worker") { + additional = "; script-src 'self' 'unsafe-inline'"; } + response.setHeader("Content-Security-Policy", + "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'" + additional, + false); // downloadable font that redirects to another site if (query["testid"] == "font-src") { @@ -90,13 +88,27 @@ function handleRequest(request, response) return; } - if (query["testid"] == "script-src-from-worker") { + if (query["testid"] == "from-worker") { // loads a script; launches a worker; that worker uses importscript; which then gets redirected // So it's: - // '); + // .. calls xhr("res=xhr-resp") + // .. calls fetch("res=xhr-resp") + response.write(''); + return; + } + + if (query["testid"] == "from-blob-worker") { + // loads a script; launches a worker; that worker uses importscript; which then gets redirected + // So it's: + // '); return; } } diff --git a/dom/security/test/csp/file_redirects_resource.sjs b/dom/security/test/csp/file_redirects_resource.sjs index b23be2f512dd..d281f19d8ba5 100644 --- a/dom/security/test/csp/file_redirects_resource.sjs +++ b/dom/security/test/csp/file_redirects_resource.sjs @@ -102,29 +102,45 @@ function handleRequest(request, response) // script that loads an internal worker that uses importScripts on a redirect // to an external script. - if (query["res"] == "loadWorkerThatImports") { + if (query["res"] == "loadWorkerThatMakesRequests") { // this creates a worker (same origin) that imports a redirecting script. - let workerURL = thisSite + resource + '?res=importScriptWorker&id=' + query["id"]; + let workerURL = thisSite + resource + '?res=makeRequestsWorker&id=' + query["id"]; response.setHeader("Content-Type", "application/javascript", false); - response.write("var w=new Worker('" + workerURL + "'); w.onmessage=function(event){ alert(event.data); }"); + response.write("new Worker('" + workerURL + "');"); + return; + } + + // script that loads an internal worker that uses importScripts on a redirect + // to an external script. + if (query["res"] == "loadBlobWorkerThatMakesRequests") { + // this creates a worker (same origin) that imports a redirecting script. + let workerURL = thisSite + resource + '?res=makeRequestsWorker&id=' + query["id"]; + response.setHeader("Content-Type", "application/javascript", false); + response.write("var x = new XMLHttpRequest(); x.open('GET', '" + workerURL + "'); "); + response.write("x.responseType = 'blob'; x.send(); "); + response.write("x.onload = () => { new Worker(URL.createObjectURL(x.response)); };"); return; } // source for a worker that simply calls importScripts on a script that // redirects. - if (query["res"] == "importScriptWorker") { + if (query["res"] == "makeRequestsWorker") { // this is code for a worker that imports a redirected script. - let scriptURL = thisSite + resource + "?redir=other&res=script&id=" + query["id"]; + let scriptURL = thisSite + resource + "?redir=other&res=script&id=script-src-redir-" + query["id"]; + let xhrURL = thisSite + resource + "?redir=other&res=xhr-resp&id=xhr-src-redir-" + query["id"]; + let fetchURL = thisSite + resource + "?redir=other&res=xhr-resp&id=fetch-src-redir-" + query["id"]; response.setHeader("Content-Type", "application/javascript", false); - response.write("importScripts('" + scriptURL + "');"); + response.write("try { importScripts('" + scriptURL + "'); } catch(ex) {} "); + response.write("var x = new XMLHttpRequest(); x.open('GET', '" + xhrURL + "'); x.send();"); + response.write("fetch('" + fetchURL + "');"); return; } // script that invokes XHR if (query["res"] == "xhr") { response.setHeader("Content-Type", "application/javascript", false); - var resp = 'var x = new XMLHttpRequest();x.open("GET", "' + otherSite + - resource+'?res=xhr-resp&testid=xhr-src-redir", false);\n' + + var resp = 'var x = new XMLHttpRequest();x.open("GET", "' + thisSite + + resource+'?redir=other&res=xhr-resp&id=xhr-src-redir", false);\n' + 'x.send(null);'; response.write(resp); return; diff --git a/dom/security/test/csp/file_worker_redirect.html b/dom/security/test/csp/file_worker_redirect.html deleted file mode 100644 index be80f5795f8c..000000000000 --- a/dom/security/test/csp/file_worker_redirect.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - Bug 949706 - CSP: Correct handling of web workers importing scripts that get redirected - - - - - diff --git a/dom/security/test/csp/file_worker_redirect.sjs b/dom/security/test/csp/file_worker_redirect.sjs deleted file mode 100644 index 6c4acd6e7a27..000000000000 --- a/dom/security/test/csp/file_worker_redirect.sjs +++ /dev/null @@ -1,37 +0,0 @@ -// testserver customized for the needs of: -// Bug 949706 - CSP: Correct handling of web workers importing scripts that get redirected - -function handleRequest(request, response) -{ - response.setHeader("Cache-Control", "no-cache", false); - response.setHeader("Content-Type", "text/html", false); - - var query = request.queryString; - - if (query === "stage_0_script_loads_worker") { - var newWorker = - "var myWorker = new Worker(\"file_worker_redirect.sjs?stage_1_worker_import_scripts\");" + - "myWorker.onmessage = function (event) { parent.checkResult(\"allowed\"); };" + - "myWorker.onerror = function (event) { parent.checkResult(\"blocked\"); };"; - response.write(newWorker); - return; - } - - if (query === "stage_1_worker_import_scripts") { - response.write("importScripts(\"file_worker_redirect.sjs?stage_2_redirect_imported_script\");"); - return; - } - - if (query === "stage_2_redirect_imported_script") { - var newLocation = - "http://test1.example.com/tests/dom/security/test/csp/file_worker_redirect.sjs?stage_3_target_script"; - response.setStatusLine("1.1", 302, "Found"); - response.setHeader("Location", newLocation, false); - return; - } - - if (query === "stage_3_target_script") { - response.write("postMessage(\"imported script loaded\");"); - return; - } -} diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini index 94966a778c2c..0b8dc68bdcca 100644 --- a/dom/security/test/csp/mochitest.ini +++ b/dom/security/test/csp/mochitest.ini @@ -40,6 +40,7 @@ support-files = file_main.html file_main.html^headers^ file_main.js + file_main_worker.js file_web_manifest.html file_web_manifest_remote.html file_web_manifest_https.html @@ -115,8 +116,6 @@ support-files = file_multi_policy_injection_bypass_2.html^headers^ file_null_baseuri.html file_form-action.html - file_worker_redirect.html - file_worker_redirect.sjs file_referrerdirective.html referrerdirective.sjs file_upgrade_insecure.html @@ -199,7 +198,6 @@ skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490) [test_referrerdirective.html] skip-if = buildapp == 'b2g' #no ssl support [test_dual_header.html] -[test_worker_redirect.html] [test_upgrade_insecure.html] # no ssl support as well as websocket tests do not work (see test_websocket.html) skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android' diff --git a/dom/security/test/csp/test_CSP.html b/dom/security/test/csp/test_CSP.html index 2b508bd0d19a..253ea8d3a769 100644 --- a/dom/security/test/csp/test_CSP.html +++ b/dom/security/test/csp/test_CSP.html @@ -25,6 +25,22 @@ window.tests = { script_bad: -1, xhr_good: -1, xhr_bad: -1, + fetch_good: -1, + fetch_bad: -1, + beacon_good: -1, + beacon_bad: -1, + worker_xhr_same_good: -1, + worker_xhr_cross_good: -1, + worker_fetch_same_good: -1, + worker_fetch_cross_good: -1, + worker_script_same_good: -1, + worker_script_cross_good: -1, + worker_inherited_xhr_good: -1, + worker_inherited_xhr_bad: -1, + worker_inherited_fetch_good: -1, + worker_inherited_fetch_bad: -1, + worker_inherited_script_good: -1, + worker_inherited_script_bad: -1, media_good: -1, media_bad: -1, font_good: -1, @@ -81,10 +97,11 @@ examiner.prototype = { window.examiner = new examiner(); window.testResult = function(testname, result, msg) { - //test already complete.... forget it... remember the first result. + // test already complete.... forget it... remember the first result. if (window.tests[testname] != -1) return; + ok(testname in window.tests, "It's a real test"); window.tests[testname] = result; is(result, true, testname + ' test: ' + msg); diff --git a/dom/security/test/csp/test_redirects.html b/dom/security/test/csp/test_redirects.html index 4656dd70deef..4a588dcbe111 100644 --- a/dom/security/test/csp/test_redirects.html +++ b/dom/security/test/csp/test_redirects.html @@ -86,10 +86,16 @@ var testExpectedResults = { "font-src": true, "worker-redir": false, "xhr-src": true, "xhr-src-redir": false, - "script-src-from-worker": true, /* test runs */ - "script-src-redir-from-worker": false, /* redir is blocked */ - "img-src-from-css": true, /* test runs */ - "img-src-redir-from-css": false, /* redir is blocked */ + "from-worker": true, + "script-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */ + "xhr-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */ + "fetch-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */ + "from-blob-worker": true, + "script-src-redir-from-blob-worker": false, + "xhr-src-redir-from-blob-worker": false, + "fetch-src-redir-from-blob-worker": false, + "img-src-from-css": true, + "img-src-redir-from-css": false, }; // takes the name of the test, the URL that was tested, and whether the diff --git a/dom/security/test/csp/test_worker_redirect.html b/dom/security/test/csp/test_worker_redirect.html deleted file mode 100644 index b45ce3823186..000000000000 --- a/dom/security/test/csp/test_worker_redirect.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - Bug 949706 - CSP: Correct handling of web workers importing scripts that get redirected - - - - - - - - - - diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index ee2b0c69f0f3..081c4d016edb 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -122,6 +122,14 @@ ChannelFromScriptURL(nsIPrincipal* principal, return NS_ERROR_DOM_SYNTAX_ERR; } + // If we have the document, use it. Unfortunately, for dedicated workers + // 'parentDoc' ends up being the parent document, which is not the document + // that we want to use. So make sure to avoid using 'parentDoc' in that + // situation. + if (parentDoc && parentDoc->NodePrincipal() != principal) { + parentDoc = nullptr; + } + int16_t shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(aContentPolicyType, uri, principal, parentDoc, @@ -164,8 +172,11 @@ ChannelFromScriptURL(nsIPrincipal* principal, aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI; nsCOMPtr channel; - // If we have the document, use it - if (parentDoc) { + // If we have the document, use it. Unfortunately, for dedicated workers + // 'parentDoc' ends up being the parent document, which is not the document + // that we want to use. So make sure to avoid using 'parentDoc' in that + // situation. + if (parentDoc && parentDoc->NodePrincipal() == principal) { rv = NS_NewChannel(getter_AddRefs(channel), uri, parentDoc,