Bug 1475849 - Refactor worker tests within test_CSP.html r=ckerschb

Summary: MozReview-Commit-ID: 8ACGbm2htCF

Reviewers: ckerschb

Reviewed By: ckerschb

Bug #: 1475849

Differential Revision: https://phabricator.services.mozilla.com/D2257

--HG--
extra : rebase_source : 3697cc7c5e73add52936b5ee08c07512e0612511
This commit is contained in:
Thomas Nguyen 2018-07-23 18:55:56 +03:00
Родитель aa6a55f066
Коммит eddde0891e
12 изменённых файлов: 664 добавлений и 155 удалений

Просмотреть файл

@ -1,39 +0,0 @@
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 == "parent") {
//Worker inherits CSP from parent worker
prefix = base + "worker_child_inherited_parent_";
suffix = base == sameBase ? "_good" : "_bad";
} else if (e.data.inherited == "document") {
//Worker inherits CSP from owner document -> parent worker -> subworker
prefix = base + "worker_child_inherited_document_";
suffix = base == sameBase ? "_good" : "_bad";
} else {
// Worker delivers CSP from HTTP header
prefix = base + "worker_child_";
suffix = base == sameBase ? "_same_bad" : "_cross_bad";
}
doXHR(prefix + "xhr" + suffix);
// Fetch is likely failed in subworker
// See Bug 1273070 - Failed to fetch in subworker
// Enable fetch test after the bug is fixed
// fetch(prefix + "xhr" + suffix);
try {
importScripts(prefix + "script" + suffix);
} catch(ex) {}
}
}

Просмотреть файл

@ -1 +0,0 @@
Content-Security-Policy: default-src 'none'

Просмотреть файл

@ -1,12 +1,8 @@
function doXHR(uri, callback) {
function doXHR(uri) {
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", uri);
xhr.responseType = "blob";
xhr.send();
xhr.onload = function () {
if (callback) callback(xhr.response);
}
} catch(ex) {}
}
@ -16,36 +12,3 @@ fetch("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=fe
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");
navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad");
var topWorkerBlob;
var nestedWorkerBlob;
doXHR("file_main_worker.js", function (topResponse) {
topWorkerBlob = URL.createObjectURL(topResponse);
doXHR("file_child_worker.js", function (response) {
nestedWorkerBlob = URL.createObjectURL(response);
runWorker();
});
});
function runWorker() {
// Top level worker, no subworker
// Worker does not inherit CSP from owner document
new Worker("file_main_worker.js").postMessage({inherited : "none"});
// Top level worker, no subworker
// Worker inherits CSP from owner document
new Worker(topWorkerBlob).postMessage({inherited : "document"});
// Subworker
// Worker does not inherit CSP from parent worker
new Worker("file_main_worker.js").postMessage({inherited : "none", nested : nestedWorkerBlob});
// Subworker
// Worker inherits CSP from parent worker
new Worker("file_main_worker.js").postMessage({inherited : "parent", nested : nestedWorkerBlob});
// Subworker
// Worker inherits CSP from owner document -> parent worker -> subworker
new Worker(topWorkerBlob).postMessage({inherited : "document", nested : nestedWorkerBlob});
}

Просмотреть файл

@ -1,48 +0,0 @@
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) => {
// Tests of nested worker
if (e.data.nested) {
if (e.data.inherited != "none") {
// Worker inherits CSP
new Worker(e.data.nested).postMessage({inherited : e.data.inherited});
}
else {
// Worker does not inherit CSP
new Worker("file_child_worker.js").postMessage({inherited : e.data.inherited});
}
return;
}
//Tests of top level worker
for (base of [sameBase, crossBase]) {
var prefix;
var suffix;
if (e.data.inherited != "none") {
// Top worker inherits CSP from owner document
prefix = base + "worker_inherited_";
suffix = base == sameBase ? "_good" : "_bad";
}
else {
// Top worker delivers CSP from HTTP header
prefix = base + "worker_";
suffix = base == sameBase ? "_same_bad" : "_cross_good";
}
doXHR(prefix + "xhr" + suffix);
fetch(prefix + "fetch" + suffix);
try {
if (e.data.inherited == "none") suffix = base == sameBase ? "_same_good" : "_cross_bad";
importScripts(prefix + "script" + suffix);
} catch(ex) {}
}
}

Просмотреть файл

@ -1 +0,0 @@
Content-Security-Policy: default-src 'self' blob: ; connect-src http://example.com

Просмотреть файл

@ -0,0 +1,439 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 1475849: Test CSP worker inheritance</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="worker_helper.js"></script>
</head>
<body>
<script type="application/javascript">
const SJS = "worker.sjs";
const SAME_BASE = "http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs";
const CROSS_BASE = "http://example.com/tests/dom/security/test/csp/file_CSP.sjs";
SimpleTest.waitForExplicitFinish();
/* test data format :
{
id: test id, short description of test,
base: URL of the request in worker,
action: type of request in worker (fetch, xhr, importscript)
type: how do we create the worker, from URL or Blob,
csp: csp of worker,
child: how do we create the child worker, from URL or Blob,
childCsp: csp of child worker
expectedBlock: result when CSP policy, true or false
}
*/
// Document's CSP is defined in main_csp_worker.html^headers^
// Content-Security-Policy: default-src 'self' blob: 'unsafe-inline'
var tests = [
// create new Worker(url), worker's csp should be deliveried from header.
// csp should be: default-src 'self' blob: ; connect-src CROSS_BASE
{
id: "worker_url_fetch_same_bad",
base: SAME_BASE,
action: "fetch",
type: "url",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: true
},
{
id: "worker_url_importScripts_same_good",
base: SAME_BASE,
action: "importScripts",
type: "url",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: false
},
{
id: "worker_url_xhr_same_bad",
base: SAME_BASE,
action: "xhr",
type: "url",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: true
},
{
id: "worker_url_fetch_cross_good",
base: CROSS_BASE,
action: "fetch",
type: "url",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: false
},
{
id: "worker_url_importScripts_cross_bad",
base: CROSS_BASE,
action: "importScripts",
type: "url",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: true
},
{
id: "worker_url_xhr_cross_good",
base: CROSS_BASE,
action: "xhr",
type: "url",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: false
},
// create new Worker(blob:), worker's csp should be inherited from
// document.
// csp should be : default-src 'self' blob: 'unsafe-inline'
{
id: "worker_blob_fetch_same_good",
base: SAME_BASE,
action: "fetch",
type: "blob",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: false
},
{
id: "worker_blob_xhr_same_good",
base: SAME_BASE,
action: "xhr",
type: "blob",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: false
},
{
id: "worker_blob_importScripts_same_good",
base: SAME_BASE,
action: "importScripts",
type: "blob",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: false
},
{
id: "worker_blob_fetch_cross_bad",
base: CROSS_BASE,
action: "fetch",
type: "blob",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: true
},
{
id: "worker_blob_xhr_cross_bad",
base: CROSS_BASE,
action: "xhr",
type: "blob",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: true
},
{
id: "worker_blob_importScripts_cross_bad",
base: CROSS_BASE,
action: "importScripts",
type: "blob",
csp: "default-src 'self' blob: ; connect-src http://example.com",
expectBlocked: true
},
// create parent worker from url, child worker from blob,
// Parent delivery csp then propagate to child
// csp should be: "default-src 'self' blob: ; connect-src 'self' http://example.com",
{
id: "worker_url_child_blob_fetch_same_good",
base: SAME_BASE,
action: "fetch",
child: "blob",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob: ; connect-src 'self' http://example.com",
expectBlocked: false
},
{
id: "worker_url_child_blob_importScripts_same_good",
base: SAME_BASE,
action: "importScripts",
child: "blob",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob: ; connect-src 'self' http://example.com",
expectBlocked: false
},
{
id: "worker_url_child_blob_xhr_same_good",
base: SAME_BASE,
child: "blob",
childCsp: "default-src 'none'",
action: "xhr",
type: "url",
csp: "default-src 'self' blob: ; connect-src 'self' http://example.com",
expectBlocked: false
},
{
id: "worker_url_child_blob_fetch_cross_good",
base: CROSS_BASE,
action: "fetch",
child: "blob",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob: ; connect-src 'self' http://example.com",
expectBlocked: false
},
{
id: "worker_url_child_blob_importScripts_cross_bad",
base: CROSS_BASE,
action: "importScripts",
child: "blob",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob: ; connect-src 'self' http://example.com",
expectBlocked: true
},
{
id: "worker_url_child_blob_xhr_cross_godd",
base: CROSS_BASE,
child: "blob",
childCsp: "default-src 'none'",
action: "xhr",
type: "url",
csp: "default-src 'self' blob: ; connect-src 'self' http://example.com",
expectBlocked: false
},
// create parent worker from blob, child worker from blob,
// Csp: document->parent->child
// csp should be : default-src 'self' blob: 'unsafe-inline'
{
id: "worker_blob_child_blob_fetch_same_good",
base: SAME_BASE,
child: "blob",
childCsp: "default-src 'none'",
action: "fetch",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: false
},
{
id: "worker_blob_child_blob_xhr_same_good",
base: SAME_BASE,
child: "blob",
childCsp: "default-src 'none'",
action: "xhr",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: false
},
{
id: "worker_blob_child_blob_importScripts_same_good",
base: SAME_BASE,
action: "importScripts",
child: "blob",
childCsp: "default-src 'none'",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: false
},
{
id: "worker_blob_child_blob_fetch_cross_bad",
base: CROSS_BASE,
child: "blob",
childCsp: "default-src 'none'",
action: "fetch",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_blob_xhr_cross_bad",
base: CROSS_BASE,
child: "blob",
childCsp: "default-src 'none'",
action: "xhr",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_blob_importScripts_cross_bad",
base: CROSS_BASE,
action: "importScripts",
child: "blob",
childCsp: "default-src 'none'",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
// create parent worker from url, child worker from url,
// child delivery csp from header
// csp should be : default-src 'none'
{
id: "worker_url_child_url_fetch_cross_bad",
base: CROSS_BASE,
action: "fetch",
child: "url",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_url_child_url_xhr_cross_bad",
base: CROSS_BASE,
child: "url",
childCsp: "default-src 'none'",
action: "xhr",
type: "url",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_url_child_url_importScripts_cross_bad",
base: CROSS_BASE,
action: "importScripts",
child: "url",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_url_child_url_fetch_same_bad",
base: SAME_BASE,
action: "fetch",
child: "url",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_url_child_url_xhr_same_bad",
base: SAME_BASE,
child: "url",
childCsp: "default-src 'none'",
action: "xhr",
type: "url",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_url_child_url_importScripts_same_bad",
base: SAME_BASE,
action: "importScripts",
child: "url",
childCsp: "default-src 'none'",
type: "url",
csp: "default-src 'self' blob:",
expectBlocked: true
},
// create parent worker from blob, child worker from url,
// child delivery csp from header
// csp should be : default-src 'none'
{
id: "worker_blob_child_url_fetch_cross_bad",
base: CROSS_BASE,
child: "url",
childCsp: "default-src 'none'",
action: "fetch",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_url_xhr_cross_bad",
base: CROSS_BASE,
child: "url",
childCsp: "default-src 'none'",
action: "xhr",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_url_importScripts_cross_bad",
base: CROSS_BASE,
action: "importScripts",
child: "url",
childCsp: "default-src 'none'",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_url_fetch_same_bad",
base: SAME_BASE,
child: "url",
childCsp: "default-src 'none'",
action: "fetch",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_url_xhr_same_bad",
base: SAME_BASE,
child: "url",
childCsp: "default-src 'none'",
action: "xhr",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
{
id: "worker_blob_child_url_importScripts_same_bad",
base: SAME_BASE,
action: "importScripts",
child: "url",
childCsp: "default-src 'none'",
type: "blob",
csp: "default-src 'self' blob:",
expectBlocked: true
},
];
async function runWorkerTest(data) {
let src = SJS;
src += "?base=" + escape(data.base);
src += "&action=" + escape(data.action);
src += "&csp=" + escape(data.csp);
src += "&id=" + escape(data.id);
if (data.child) {
src += "&child=" + escape(data.child);
}
if (data.childCsp) {
src += "&childCsp=" + escape(data.childCsp);
}
switch (data.type) {
case "url":
new Worker(src);
break;
case "blob":
new Worker(URL.createObjectURL(await doXHRGetBlob(src)));
break;
default:
throw "Unsupport type";
}
let checkUri = data.base + "?id=" + data.id;
await assertCSPBlock(checkUri, data.expectBlocked);
runNextTest();
};
tests.forEach(function(test) {
addAsyncTest(async function() {
runWorkerTest(test);
});
});
runNextTest();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1 @@
Content-Security-Policy: default-src 'self' blob: 'unsafe-inline'

Просмотреть файл

@ -47,10 +47,6 @@ support-files =
file_main.html
file_main.html^headers^
file_main.js
file_main_worker.js
file_main_worker.js^headers^
file_child_worker.js
file_child_worker.js^headers^
file_web_manifest.html
file_web_manifest_remote.html
file_web_manifest_https.html
@ -363,3 +359,9 @@ support-files =
file_frame_src.js
file_frame_src_inner.html
[test_security_policy_violation_event.html]
[test_csp_worker_inheritance.html]
support-files =
worker.sjs
worker_helper.js
main_csp_worker.html
main_csp_worker.html^headers^

Просмотреть файл

@ -29,30 +29,6 @@ window.tests = {
fetch_bad: -1,
beacon_good: -1,
beacon_bad: -1,
worker_xhr_same_bad: -1,
worker_xhr_cross_good: -1,
worker_fetch_same_bad: -1,
worker_fetch_cross_good: -1,
worker_script_same_good: -1,
worker_script_cross_bad: -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,
worker_child_xhr_same_bad: -1,
worker_child_xhr_cross_bad: -1,
worker_child_script_same_bad: -1,
worker_child_script_cross_bad: -1,
worker_child_inherited_parent_xhr_bad: -1,
worker_child_inherited_parent_xhr_good: -1,
worker_child_inherited_parent_script_good: -1,
worker_child_inherited_parent_script_bad: -1,
worker_child_inherited_document_xhr_good: -1,
worker_child_inherited_document_xhr_bad: -1,
worker_child_inherited_document_script_good: -1,
worker_child_inherited_document_script_bad: -1,
media_good: -1,
media_bad: -1,
font_good: -1,

Просмотреть файл

@ -0,0 +1,20 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
<html>
<head>
<title>Test for Bug 1475849</title>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
<script class="testbody" type="text/javascript">
document.getElementById('cspframe').src = 'main_csp_worker.html';
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,116 @@
Components.utils.importGlobalProperties(["URLSearchParams"]);
const SJS = "http://mochi.test:8888/tests/dom/security/test/csp/worker.sjs";
function createFetchWorker(url)
{
return `fetch("${url}");`;
}
function createXHRWorker(url)
{
return `
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", "${url}");
xhr.send();
} catch(ex) {}
`;
}
function createImportScriptsWorker(url)
{
return `
try {
importScripts("${url}");
} catch(ex) {}
`;
}
function createChildWorkerURL(params)
{
let url = SJS + "?" + params.toString();
return `new Worker("${url}");`;
}
function createChildWorkerBlob(params)
{
let url = SJS + "?" + params.toString();
return `
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", "${url}");
xhr.responseType = "blob";
xhr.send();
xhr.onload = () => {
new Worker(URL.createObjectURL(xhr.response));};
} catch(ex) {}
`;
}
function handleRequest(request, response)
{
let params = new URLSearchParams(request.queryString);
let id = params.get("id");
let base = unescape(params.get("base"));
let child = params.has("child") ? params.get("child") : "";
//avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "application/javascript");
// Deliver the CSP policy encoded in the URL
if(params.has("csp")) {
response.setHeader("Content-Security-Policy", unescape(params.get("csp")), false);
}
if (child) {
let childCsp = params.has("childCsp") ? params.get("childCsp") : "";
params.delete("csp");
params.delete("child");
params.delete("childCsp");
params.append("csp", childCsp);
switch (child) {
case "blob":
response.write(createChildWorkerBlob(params));
break;
case "url":
response.write(createChildWorkerURL(params));
break;
default:
response.setStatusLine(request.httpVersion, 400, "Bad request");
break;
}
return;
}
if (params.has("action")) {
switch (params.get("action")) {
case "fetch":
response.write(createFetchWorker(base + "?id=" + id));
break;
case "xhr":
response.write(createXHRWorker(base + "?id=" + id));
break;
case "importScripts":
response.write(createImportScriptsWorker(base + "?id=" + id));
break;
default:
response.setStatusLine(request.httpVersion, 400, "Bad request");
break;
}
return;
}
response.write("I don't know action ");
return;
}

Просмотреть файл

@ -0,0 +1,81 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var _tests = [];
function addTest(test) {
_tests.push(test);
}
function addAsyncTest(fn) {
_tests.push(() => (fn)().catch(ok.bind(null, false)));
}
function runNextTest() {
if (_tests.length == 0) {
SimpleTest.finish();
return;
}
const fn = _tests.shift();
try {
fn();
} catch (ex) {
info("Test function " + (fn.name ? "'" + fn.name + "' " : "") +
"threw an exception: " + ex);
}
}
/**
* Helper to perform an XHR then blob response to create worker
*/
function doXHRGetBlob(uri) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open("GET", uri);
xhr.responseType = "blob";
xhr.addEventListener("load", function() {
is(xhr.status, 200, "doXHRGetBlob load uri='" + uri + "' status=" + xhr.status);
resolve(xhr.response);
});
xhr.send();
});
}
function removeObserver(observer) {
SpecialPowers.removeObserver(observer, "specialpowers-http-notify-request");
SpecialPowers.removeObserver(observer, "csp-on-violate-policy");
}
/**
* Helper to perform an assert to check if the request should be blocked or
* allowed by CSP
*/
function assertCSPBlock(url, shouldBlock) {
return new Promise((resolve, reject) => {
let observer = {
observe(subject, topic, data) {
if (topic === "specialpowers-http-notify-request") {
if (data == url) {
is(shouldBlock, false, "Should allow request uri='" + url);
removeObserver(observer);
resolve();
}
}
if (topic === "csp-on-violate-policy") {
let asciiSpec = SpecialPowers.getPrivilegedProps(
SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
if (asciiSpec == url) {
is(shouldBlock, true, "Should block request uri='" + url);
removeObserver(observer);
resolve();
}
}
},
};
SpecialPowers.addObserver(observer, "csp-on-violate-policy");
SpecialPowers.addObserver(observer, "specialpowers-http-notify-request");
});
}