CSP3: Implement 'worker-src'.
As a drive-by, this also renames `allowChildFrameFromSource` to
`allowFrameFromSource`, and `allowChildContextFromSource` to
`allowWorkerFromSource` to reflect their usage.
Intent to Ship: https://groups.google.com/a/chromium.org/d/msg/blink-dev/1UkZE-vOROc/gEj7psewAAAJ
BUG=662930
Review-Url: https://codereview.chromium.org/2480303002
Cr-Commit-Position: refs/heads/master@{#431228}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-blocked-expected.txt
index dfd5cb3..f15c63e 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-blocked-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 1: Refused to create a child context containing 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/alert-fail.js' because it violates the following Content Security Policy directive: "child-src 'none'".
+CONSOLE ERROR: line 1: Refused to create a worker from 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/alert-fail.js' because it violates the following Content Security Policy directive: "child-src 'none'".
Workers should be governed by 'child-src'.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-shared-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-shared-blocked-expected.txt
index 02e2545..df93a58 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-shared-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/1.1/child-src/worker-shared-blocked-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 1: Refused to create a child context containing 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/alert-fail.js' because it violates the following Content Security Policy directive: "child-src 'none'".
+CONSOLE ERROR: line 1: Refused to create a worker from 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/alert-fail.js' because it violates the following Content Security Policy directive: "child-src 'none'".
SharedWorkers should be governed by 'child-src'.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/resources/ping.js b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/resources/ping.js
new file mode 100644
index 0000000..750ae45f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/resources/ping.js
@@ -0,0 +1,12 @@
+if (typeof ServiceWorkerGlobalScope === "function") {
+ self.onmessage = function (e) { e.source.postMessage("ping"); };
+} else if (typeof SharedWorkerGlobalScope === "function") {
+ onconnect = function (e) {
+ var port = e.ports[0];
+
+ port.onmessage = function () { port.postMessage("ping"); }
+ port.postMessage("ping");
+ };
+} else if (typeof DedicatedWorkerGlobalScope === "function") {
+ self.postMessage("ping");
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/resources/testharness-helper.js b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/resources/testharness-helper.js
new file mode 100644
index 0000000..a1eaa891e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/resources/testharness-helper.js
@@ -0,0 +1,131 @@
+function assert_no_csp_event_for_url(test, url) {
+ document.addEventListener("securitypolicyviolation", test.step_func(e => {
+ if (e.blockedURI !== url)
+ return;
+ assert_unreached("SecurityPolicyViolation event fired for " + url);
+ }));
+}
+
+function assert_no_event(test, obj, name) {
+ obj.addEventListener(name, test.unreached_func("The '" + name + "' event should not have fired."));
+}
+
+function waitUntilCSPEventForURL(test, url) {
+ return new Promise((resolve, reject) => {
+ document.addEventListener("securitypolicyviolation", test.step_func(e => {
+ if (e.blockedURI == url)
+ resolve(e);
+ }));
+ });
+}
+
+function waitUntilEvent(obj, name) {
+ return new Promise((resolve, reject) => {
+ obj.addEventListener(name, resolve);
+ });
+}
+
+// Given the URL of a worker that pings its opener upon load, this
+// function builds a test that asserts that the ping is received,
+// and that no CSP event fires.
+function assert_worker_is_loaded(url, description) {
+ async_test(t => {
+ assert_no_csp_event_for_url(t, url);
+ var w = new Worker(url);
+ assert_no_event(t, w, "error");
+ waitUntilEvent(w, "message")
+ .then(t.step_func_done(e => {
+ assert_equals(e.data, "ping");
+ }));
+ }, description);
+}
+
+function assert_shared_worker_is_loaded(url, description) {
+ async_test(t => {
+ assert_no_csp_event_for_url(t, url);
+ var w = new SharedWorker(url);
+ assert_no_event(t, w, "error");
+ waitUntilEvent(w.port, "message")
+ .then(t.step_func_done(e => {
+ assert_equals(e.data, "ping");
+ }));
+ w.port.start();
+ }, description);
+}
+
+function assert_service_worker_is_loaded(url, description) {
+ promise_test(t => {
+ assert_no_csp_event_for_url(t, url);
+ return Promise.all([
+ waitUntilEvent(navigator.serviceWorker, "message")
+ .then(e => {
+ assert_equals(e.data, "ping");
+ }),
+ navigator.serviceWorker.register(url, { scope: url })
+ .then(r => {
+ var sw = r.active || r.installing || r.waiting;
+ t.add_cleanup(_ => r.unregister());
+ sw.postMessage("pong?");
+ })
+ ]);
+ }, description);
+}
+
+// Given the URL of a worker that pings its opener upon load, this
+// function builds a test that asserts that the constructor throws
+// a SecurityError, and that a CSP event fires.
+function assert_worker_is_blocked(url, description) {
+ async_test(t => {
+ // If |url| is a blob, it will be stripped down to "blob" for reporting.
+ var reportedURL = new URL(url).protocol == "blob:" ? "blob" : url;
+ waitUntilCSPEventForURL(t, reportedURL)
+ .then(t.step_func_done(e => {
+ assert_equals(e.blockedURI, reportedURL);
+ assert_equals(e.violatedDirective, "worker-src");
+ assert_equals(e.effectiveDirective, "worker-src");
+ }));
+
+ // TODO(mkwst): We shouldn't be throwing here. We should be firing an
+ // `error` event on the Worker. https://crbug.com/663298
+ assert_throws("SecurityError", function () {
+ var w = new Worker(url);
+ });
+ }, description);
+}
+
+function assert_shared_worker_is_blocked(url, description) {
+ async_test(t => {
+ // If |url| is a blob, it will be stripped down to "blob" for reporting.
+ var reportedURL = new URL(url).protocol == "blob:" ? "blob" : url;
+ waitUntilCSPEventForURL(t, reportedURL)
+ .then(t.step_func_done(e => {
+ assert_equals(e.blockedURI, reportedURL);
+ assert_equals(e.violatedDirective, "worker-src");
+ assert_equals(e.effectiveDirective, "worker-src");
+ }));
+
+ // TODO(mkwst): We shouldn't be throwing here. We should be firing an
+ // `error` event on the SharedWorker. https://crbug.com/663298
+ assert_throws("SecurityError", function () {
+ var w = new SharedWorker(url);
+ });
+ }, description);
+}
+
+function assert_service_worker_is_blocked(url, description) {
+ promise_test(t => {
+ assert_no_event(t, navigator.serviceWorker, "message");
+ // If |url| is a blob, it will be stripped down to "blob" for reporting.
+ var reportedURL = new URL(url).protocol == "blob:" ? "blob" : url;
+ return Promise.all([
+ waitUntilCSPEventForURL(t, reportedURL)
+ .then(t.step_func_done(e => {
+ assert_equals(e.blockedURI, reportedURL);
+ assert_equals(e.violatedDirective, "worker-src");
+ assert_equals(e.effectiveDirective, "worker-src");
+ })),
+ promise_rejects(t, "SecurityError", navigator.serviceWorker.register(url, { scope: url }))
+ ]);
+ }, description);
+}
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/service-worker-blocked-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/service-worker-blocked-expected.txt
index 212517a..935ff994 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/service-worker-blocked-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/service-worker-blocked-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: line 9: Refused to create a child context containing 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/service-worker.js' because it violates the following Content Security Policy directive: "child-src 'none'".
+CONSOLE ERROR: line 9: Refused to create a worker from 'http://127.0.0.1:8000/security/contentSecurityPolicy/resources/service-worker.js' because it violates the following Content Security Policy directive: "child-src 'none'".
This is a testharness.js-based test.
PASS Test that a service worker cannot be registered if the CSP does not allow it
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-child.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-child.html
new file mode 100644
index 0000000..da1fbc1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="child-src http://127.0.0.1:8000 blob:">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_worker_is_loaded(url, "Same-origin dedicated worker allowed by host-source expression.");
+
+ var b = new Blob(["postMessage('ping');"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_worker_is_loaded(url, "blob: dedicated worker allowed by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-fallback.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-fallback.html
new file mode 100644
index 0000000..6de1985
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-fallback.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src http://127.0.0.1:8000 blob:; child-src 'none'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_worker_is_loaded(url, "Same-origin dedicated worker allowed by host-source expression.");
+
+ var b = new Blob(["postMessage('ping');"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_worker_is_loaded(url, "blob: dedicated worker allowed by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-list.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-list.html
new file mode 100644
index 0000000..8b19f53
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-list.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src http://127.0.0.1:8000 blob:">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_worker_is_loaded(url, "Same-origin dedicated worker allowed by host-source expression.");
+
+ var b = new Blob(["postMessage('ping');"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_worker_is_loaded(url, "blob: dedicated worker allowed by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-none.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-none.html
new file mode 100644
index 0000000..3377775
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-none.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src 'none'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_worker_is_blocked(url, "Same-origin dedicated worker blocked by host-source expression.");
+
+ var b = new Blob(["postMessage('ping');"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_worker_is_blocked(url, "blob: dedicated worker blocked by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-self.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-self.html
new file mode 100644
index 0000000..b504a6de
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/dedicated-self.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src 'self'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_worker_is_loaded(url, "Same-origin dedicated worker allowed by 'self'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-child.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-child.html
new file mode 100644
index 0000000..9b897fc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-child.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="child-src http://127.0.0.1:8000">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_service_worker_is_loaded(url, "Same-origin service worker allowed by host-source expression.");
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-fallback.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-fallback.html
new file mode 100644
index 0000000..3ce8e542
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-fallback.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src http://127.0.0.1:8000; child-src 'none'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_service_worker_is_loaded(url, "Same-origin service worker allowed by host-source expression.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-list.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-list.html
new file mode 100644
index 0000000..6f71549
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-list.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src http://127.0.0.1:8000">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_service_worker_is_loaded(url, "Same-origin service worker allowed by host-source expression.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-none.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-none.html
new file mode 100644
index 0000000..2b239e2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-none.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src 'none'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_service_worker_is_blocked(url, "Same-origin service worker blocked by 'none'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-self.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-self.html
new file mode 100644
index 0000000..5fdccae
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/service-self.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src 'self'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_service_worker_is_loaded(url, "Same-origin service worker allowed by 'self'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-child.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-child.html
new file mode 100644
index 0000000..8376498
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="child-src http://127.0.0.1:8000 blob:">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_shared_worker_is_loaded(url, "Same-origin dedicated worker allowed by 'self'.");
+
+ var b = new Blob(["onconnect = e => { e.ports[0].postMessage('ping'); }"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_shared_worker_is_loaded(url, "blob: dedicated worker allowed by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-fallback.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-fallback.html
new file mode 100644
index 0000000..2a8754b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-fallback.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src http://127.0.0.1:8000 blob:; child-src 'none'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_shared_worker_is_loaded(url, "Same-origin dedicated worker allowed by 'self'.");
+
+ var b = new Blob(["onconnect = e => { e.ports[0].postMessage('ping'); }"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_shared_worker_is_loaded(url, "blob: dedicated worker allowed by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-list.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-list.html
new file mode 100644
index 0000000..43d7481
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-list.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src http://127.0.0.1:8000 blob:">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_shared_worker_is_loaded(url, "Same-origin dedicated worker allowed by 'self'.");
+
+ var b = new Blob(["onconnect = e => { e.ports[0].postMessage('ping'); }"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_shared_worker_is_loaded(url, "blob: dedicated worker allowed by 'blob:'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-none.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-none.html
new file mode 100644
index 0000000..9a24cf7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-none.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src 'none'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_shared_worker_is_blocked(url, "Same-origin shared worker blocked by 'none'.");
+
+ var b = new Blob(["onconnect = e => { e.ports[0].postMessage('ping'); }"], {type: "text/javascript"});
+ var url = URL.createObjectURL(b);
+ assert_shared_worker_is_blocked(url, "blob: shared worker blocked by 'none'.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-self.html b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-self.html
new file mode 100644
index 0000000..42d3da2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/worker-src/shared-self.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/testharness-helper.js"></script>
+<meta http-equiv="Content-Security-Policy" content="worker-src 'self'">
+<script>
+ var url = new URL("/security/contentSecurityPolicy/resources/ping.js", document.baseURI).toString();
+ assert_shared_worker_is_loaded(url, "Same-origin dedicated worker allowed by 'self'.");
+</script>
+
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
index 9d232e9..4bdfefc 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
@@ -430,11 +430,17 @@
if (checkSource(directive, url, redirectStatus) && !checkDynamic(directive))
return true;
+ // We should never have a violation against `child-src` or `default-src`
+ // directly; the effective directive should always be one of the explicit
+ // fetch directives.
+ DCHECK_NE(ContentSecurityPolicy::ChildSrc, effectiveDirective);
+ DCHECK_NE(ContentSecurityPolicy::DefaultSrc, effectiveDirective);
+
String prefix;
if (ContentSecurityPolicy::BaseURI == effectiveDirective)
prefix = "Refused to set the document's base URI to '";
- else if (ContentSecurityPolicy::ChildSrc == effectiveDirective)
- prefix = "Refused to create a child context containing '";
+ else if (ContentSecurityPolicy::WorkerSrc == effectiveDirective)
+ prefix = "Refused to create a worker from '";
else if (ContentSecurityPolicy::ConnectSrc == effectiveDirective)
prefix = "Refused to connect to '";
else if (ContentSecurityPolicy::FontSrc == effectiveDirective)
@@ -626,15 +632,14 @@
redirectStatus);
}
-bool CSPDirectiveList::allowChildFrameFromSource(
+bool CSPDirectiveList::allowFrameFromSource(
const KURL& url,
ResourceRequest::RedirectStatus redirectStatus,
ContentSecurityPolicy::ReportingStatus reportingStatus) const {
if (url.protocolIsAbout())
return true;
- // 'frame-src' is the only directive which overrides something other than the
- // default sources. It overrides 'child-src', which overrides the default
+ // 'frame-src' overrides 'child-src', which overrides the default
// sources. So, we do this nested set of calls to 'operativeDirective()' to
// grab 'frame-src' if it exists, 'child-src' if it doesn't, and 'defaut-src'
// if neither are available.
@@ -745,16 +750,22 @@
: checkSource(m_baseURI.get(), url, redirectStatus);
}
-bool CSPDirectiveList::allowChildContextFromSource(
+bool CSPDirectiveList::allowWorkerFromSource(
const KURL& url,
ResourceRequest::RedirectStatus redirectStatus,
ContentSecurityPolicy::ReportingStatus reportingStatus) const {
+ // 'worker-src' overrides 'child-src', which overrides the default
+ // sources. So, we do this nested set of calls to 'operativeDirective()' to
+ // grab 'worker-src' if it exists, 'child-src' if it doesn't, and 'defaut-src'
+ // if neither are available.
+ SourceListDirective* whichDirective = operativeDirective(
+ m_workerSrc.get(), operativeDirective(m_childSrc.get()));
+
return reportingStatus == ContentSecurityPolicy::SendReport
- ? checkSourceAndReportViolation(
- operativeDirective(m_childSrc.get()), url,
- ContentSecurityPolicy::ChildSrc, redirectStatus)
- : checkSource(operativeDirective(m_childSrc.get()), url,
- redirectStatus);
+ ? checkSourceAndReportViolation(whichDirective, url,
+ ContentSecurityPolicy::WorkerSrc,
+ redirectStatus)
+ : checkSource(whichDirective, url, redirectStatus);
}
bool CSPDirectiveList::allowAncestors(
@@ -1106,6 +1117,8 @@
setCSPDirective<SourceListDirective>(name, value, m_baseURI);
} else if (equalIgnoringCase(name, ContentSecurityPolicy::ChildSrc)) {
setCSPDirective<SourceListDirective>(name, value, m_childSrc);
+ } else if (equalIgnoringCase(name, ContentSecurityPolicy::WorkerSrc)) {
+ setCSPDirective<SourceListDirective>(name, value, m_workerSrc);
} else if (equalIgnoringCase(name, ContentSecurityPolicy::FormAction)) {
setCSPDirective<SourceListDirective>(name, value, m_formAction);
} else if (equalIgnoringCase(name, ContentSecurityPolicy::PluginTypes)) {
@@ -1146,6 +1159,7 @@
visitor->trace(m_objectSrc);
visitor->trace(m_scriptSrc);
visitor->trace(m_styleSrc);
+ visitor->trace(m_workerSrc);
}
} // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
index c4c9215..b305e5b 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
@@ -83,9 +83,9 @@
bool allowObjectFromSource(const KURL&,
ResourceRequest::RedirectStatus,
ContentSecurityPolicy::ReportingStatus) const;
- bool allowChildFrameFromSource(const KURL&,
- ResourceRequest::RedirectStatus,
- ContentSecurityPolicy::ReportingStatus) const;
+ bool allowFrameFromSource(const KURL&,
+ ResourceRequest::RedirectStatus,
+ ContentSecurityPolicy::ReportingStatus) const;
bool allowImageFromSource(const KURL&,
ResourceRequest::RedirectStatus,
ContentSecurityPolicy::ReportingStatus) const;
@@ -107,10 +107,9 @@
bool allowBaseURI(const KURL&,
ResourceRequest::RedirectStatus,
ContentSecurityPolicy::ReportingStatus) const;
- bool allowChildContextFromSource(
- const KURL&,
- ResourceRequest::RedirectStatus,
- ContentSecurityPolicy::ReportingStatus) const;
+ bool allowWorkerFromSource(const KURL&,
+ ResourceRequest::RedirectStatus,
+ ContentSecurityPolicy::ReportingStatus) const;
// |allowAncestors| does not need to know whether the resource was a
// result of a redirect. After a redirect, source paths are usually
// ignored to stop a page from learning the path to which the
@@ -294,6 +293,7 @@
Member<SourceListDirective> m_objectSrc;
Member<SourceListDirective> m_scriptSrc;
Member<SourceListDirective> m_styleSrc;
+ Member<SourceListDirective> m_workerSrc;
uint8_t m_requireSRIFor;
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp
index de8284a..2fcde83 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp
@@ -391,4 +391,48 @@
}
}
+TEST_F(CSPDirectiveListTest, workerSrc) {
+ struct TestCase {
+ const char* list;
+ bool allowed;
+ } cases[] = {
+ {"worker-src 'none'", false},
+ {"worker-src http://not.example.test", false},
+ {"worker-src https://example.test", true},
+ {"default-src *; worker-src 'none'", false},
+ {"default-src *; worker-src http://not.example.test", false},
+ {"default-src *; worker-src https://example.test", true},
+ {"child-src *; worker-src 'none'", false},
+ {"child-src *; worker-src http://not.example.test", false},
+ {"child-src *; worker-src https://example.test", true},
+ {"default-src *; child-src *; worker-src 'none'", false},
+ {"default-src *; child-src *; worker-src http://not.example.test", false},
+ {"default-src *; child-src *; worker-src https://example.test", true},
+
+ // Fallback to child-src.
+ {"child-src 'none'", false},
+ {"child-src http://not.example.test", false},
+ {"child-src https://example.test", true},
+ {"default-src *; child-src 'none'", false},
+ {"default-src *; child-src http://not.example.test", false},
+ {"default-src *; child-src https://example.test", true},
+
+ // Fallback to default-src.
+ {"default-src 'none'", false},
+ {"default-src http://not.example.test", false},
+ {"default-src https://example.test", true},
+ };
+
+ for (const auto& test : cases) {
+ SCOPED_TRACE(test.list);
+ KURL resource = KURL(KURL(), "https://example.test/worker.js");
+ Member<CSPDirectiveList> directiveList =
+ createList(test.list, ContentSecurityPolicyHeaderTypeEnforce);
+ EXPECT_EQ(test.allowed,
+ directiveList->allowWorkerFromSource(
+ resource, ResourceRequest::RedirectStatus::NoRedirect,
+ ContentSecurityPolicy::SuppressReport));
+ }
+}
+
} // namespace blink
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
index 52ef1de..3450842 100644
--- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
@@ -92,9 +92,9 @@
const char ContentSecurityPolicy::FrameAncestors[] = "frame-ancestors";
const char ContentSecurityPolicy::PluginTypes[] = "plugin-types";
-// CSP Editor's Draft:
-// https://w3c.github.io/webappsec/specs/content-security-policy
+// CSP Level 3 Directives
const char ContentSecurityPolicy::ManifestSrc[] = "manifest-src";
+const char ContentSecurityPolicy::WorkerSrc[] = "worker-src";
// Mixed Content Directive
// https://w3c.github.io/webappsec/specs/mixedcontent/#strict-mode
@@ -125,6 +125,7 @@
equalIgnoringCase(name, FrameAncestors) ||
equalIgnoringCase(name, PluginTypes) ||
equalIgnoringCase(name, ManifestSrc) ||
+ equalIgnoringCase(name, WorkerSrc) ||
equalIgnoringCase(name, BlockAllMixedContent) ||
equalIgnoringCase(name, UpgradeInsecureRequests) ||
equalIgnoringCase(name, TreatAsPublicAddress) ||
@@ -844,7 +845,7 @@
return allowFormAction(url, redirectStatus, reportingStatus);
case WebURLRequest::RequestContextFrame:
case WebURLRequest::RequestContextIframe:
- return allowChildFrameFromSource(url, redirectStatus, reportingStatus);
+ return allowFrameFromSource(url, redirectStatus, reportingStatus);
case WebURLRequest::RequestContextImport:
case WebURLRequest::RequestContextScript:
return allowScriptFromSource(url, nonce, parserDisposition,
@@ -892,11 +893,11 @@
m_policies, url, redirectStatus, reportingStatus);
}
-bool ContentSecurityPolicy::allowChildFrameFromSource(
+bool ContentSecurityPolicy::allowFrameFromSource(
const KURL& url,
RedirectStatus redirectStatus,
ContentSecurityPolicy::ReportingStatus reportingStatus) const {
- return isAllowedByAll<&CSPDirectiveList::allowChildFrameFromSource>(
+ return isAllowedByAll<&CSPDirectiveList::allowFrameFromSource>(
m_policies, url, redirectStatus, reportingStatus);
}
@@ -971,7 +972,7 @@
// impact of this backwards-incompatible change.
if (Document* document = this->document()) {
UseCounter::count(*document, UseCounter::WorkerSubjectToCSP);
- if (isAllowedByAll<&CSPDirectiveList::allowChildContextFromSource>(
+ if (isAllowedByAll<&CSPDirectiveList::allowWorkerFromSource>(
m_policies, url, redirectStatus, SuppressReport) &&
!isAllowedByAll<&CSPDirectiveList::allowScriptFromSource>(
m_policies, url, AtomicString(), NotParserInserted, redirectStatus,
@@ -981,7 +982,7 @@
}
}
- return isAllowedByAll<&CSPDirectiveList::allowChildContextFromSource>(
+ return isAllowedByAll<&CSPDirectiveList::allowWorkerFromSource>(
m_policies, url, redirectStatus, reportingStatus);
}
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h
index 2bb64ac..87ee3397 100644
--- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h
+++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h
@@ -92,9 +92,9 @@
static const char FrameAncestors[];
static const char PluginTypes[];
- // Manifest Directives (to be merged into CSP Level 2)
- // https://w3c.github.io/manifest/#content-security-policy
+ // CSP Level 3 Directives
static const char ManifestSrc[];
+ static const char WorkerSrc[];
// Mixed Content Directive
// https://w3c.github.io/webappsec/specs/mixedcontent/#strict-mode
@@ -182,9 +182,9 @@
bool allowObjectFromSource(const KURL&,
RedirectStatus = RedirectStatus::NoRedirect,
ReportingStatus = SendReport) const;
- bool allowChildFrameFromSource(const KURL&,
- RedirectStatus = RedirectStatus::NoRedirect,
- ReportingStatus = SendReport) const;
+ bool allowFrameFromSource(const KURL&,
+ RedirectStatus = RedirectStatus::NoRedirect,
+ ReportingStatus = SendReport) const;
bool allowImageFromSource(const KURL&,
RedirectStatus = RedirectStatus::NoRedirect,
ReportingStatus = SendReport) const;
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index 23c0121c..d9ba74a92 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -1596,8 +1596,8 @@
if (parentFrame) {
ContentSecurityPolicy* parentPolicy =
parentFrame->securityContext()->contentSecurityPolicy();
- if (!parentPolicy->allowChildFrameFromSource(request.url(),
- request.redirectStatus())) {
+ if (!parentPolicy->allowFrameFromSource(request.url(),
+ request.redirectStatus())) {
// Fire a load event, as timing attacks would otherwise reveal that the
// frame was blocked. This way, it looks like every other cross-origin
// page load.