summaryrefslogtreecommitdiffstats
path: root/dom
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-03-03 11:21:43 +0100
committerwolfbeast <mcwerewolf@gmail.com>2018-03-03 11:22:15 +0100
commitc3039dadd95f5487e84311a9719604fa901aacd7 (patch)
tree3168b0b2d41184b89f894821e25ca258d88d6af4 /dom
parent8891f99913d9054c363c0266cf4ee9718cbf474e (diff)
downloadUXP-c3039dadd95f5487e84311a9719604fa901aacd7.tar
UXP-c3039dadd95f5487e84311a9719604fa901aacd7.tar.gz
UXP-c3039dadd95f5487e84311a9719604fa901aacd7.tar.lz
UXP-c3039dadd95f5487e84311a9719604fa901aacd7.tar.xz
UXP-c3039dadd95f5487e84311a9719604fa901aacd7.zip
Add support for CSP v3 "worker-src" directive
Diffstat (limited to 'dom')
-rw-r--r--dom/interfaces/security/nsIContentSecurityPolicy.idl1
-rw-r--r--dom/locales/en-US/chrome/security/csp.properties7
-rw-r--r--dom/security/nsCSPParser.cpp47
-rw-r--r--dom/security/nsCSPParser.h19
-rw-r--r--dom/security/nsCSPUtils.cpp64
-rw-r--r--dom/security/nsCSPUtils.h47
-rw-r--r--dom/security/test/csp/file_frame_src.js14
-rw-r--r--dom/security/test/csp/file_frame_src_child_governs.html10
-rw-r--r--dom/security/test/csp/file_frame_src_frame_governs.html10
-rw-r--r--dom/security/test/csp/file_frame_src_inner.html5
-rw-r--r--dom/security/test/csp/file_spawn_service_worker.js1
-rw-r--r--dom/security/test/csp/file_spawn_shared_worker.js7
-rw-r--r--dom/security/test/csp/file_spawn_worker.js1
-rw-r--r--dom/security/test/csp/file_worker_src.js52
-rw-r--r--dom/security/test/csp/file_worker_src_child_governs.html9
-rw-r--r--dom/security/test/csp/file_worker_src_script_governs.html9
-rw-r--r--dom/security/test/csp/file_worker_src_worker_governs.html9
-rw-r--r--dom/security/test/csp/mochitest.ini15
-rw-r--r--dom/security/test/csp/test_child-src_worker.html6
-rw-r--r--dom/security/test/csp/test_frame_src.html84
-rw-r--r--dom/security/test/csp/test_worker_src.html94
-rw-r--r--dom/security/test/gtest/TestCSPParser.cpp4
-rw-r--r--dom/webidl/CSPDictionaries.webidl1
23 files changed, 469 insertions, 47 deletions
diff --git a/dom/interfaces/security/nsIContentSecurityPolicy.idl b/dom/interfaces/security/nsIContentSecurityPolicy.idl
index 51ca46f2a..da4297f33 100644
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -61,6 +61,7 @@ interface nsIContentSecurityPolicy : nsISerializable
const unsigned short BLOCK_ALL_MIXED_CONTENT = 19;
const unsigned short REQUIRE_SRI_FOR = 20;
const unsigned short SANDBOX_DIRECTIVE = 21;
+ const unsigned short WORKER_SRC_DIRECTIVE = 22;
/**
* Accessor method for a read-only string version of the policy at a given
diff --git a/dom/locales/en-US/chrome/security/csp.properties b/dom/locales/en-US/chrome/security/csp.properties
index 4124ef8aa..4c4054cee 100644
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -112,9 +112,10 @@ couldntParsePort = Couldn’t parse port in %1$S
# LOCALIZATION NOTE (duplicateDirective):
# %1$S is the name of the duplicate directive
duplicateDirective = Duplicate %1$S directives detected. All but the first instance will be ignored.
-# LOCALIZATION NOTE (deprecatedDirective):
-# %1$S is the name of the deprecated directive, %2$S is the name of the replacement.
-deprecatedDirective = Directive ‘%1$S’ has been deprecated. Please use directive ‘%2$S’ instead.
+# LOCALIZATION NOTE (deprecatedChildSrcDirective):
+# %1$S is the value of the deprecated directive.
+# Do not localize: worker-src, frame-src
+deprecatedChildSrcDirective = Directive ‘%1$S’ has been deprecated. Please use directive ‘worker-src’ to control workers, or directive ‘frame-src’ to control frames respectively.
# LOCALIZATION NOTE (couldntParseInvalidSandboxFlag):
# %1$S is the option that could not be understood
couldntParseInvalidSandboxFlag = Couldn’t parse invalid sandbox flag ‘%1$S’
diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp
index 86aa4e001..a0eba6918 100644
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -136,6 +136,8 @@ nsCSPParser::nsCSPParser(cspTokens& aTokens,
, mUnsafeInlineKeywordSrc(nullptr)
, mChildSrc(nullptr)
, mFrameSrc(nullptr)
+ , mWorkerSrc(nullptr)
+ , mScriptSrc(nullptr)
, mParsingFrameAncestorsDir(false)
, mTokens(aTokens)
, mSelfURI(aSelfURI)
@@ -1099,21 +1101,37 @@ nsCSPParser::directiveName()
return new nsUpgradeInsecureDirective(CSP_StringToCSPDirective(mCurToken));
}
- // child-src has it's own class to handle frame-src if necessary
+ // child-src by itself is deprecatd but will be enforced
+ // * for workers (if worker-src is not explicitly specified)
+ // * for frames (if frame-src is not explicitly specified)
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE)) {
+ const char16_t* params[] = { mCurToken.get() };
+ logWarningErrorToConsole(nsIScriptError::warningFlag,
+ "deprecatedChildSrcDirective",
+ params, ArrayLength(params));
mChildSrc = new nsCSPChildSrcDirective(CSP_StringToCSPDirective(mCurToken));
return mChildSrc;
}
- // if we have a frame-src, cache it so we can decide whether to use child-src
+ // if we have a frame-src, cache it so we can discard child-src for frames
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE)) {
- const char16_t* params[] = { mCurToken.get(), NS_LITERAL_STRING("child-src").get() };
- logWarningErrorToConsole(nsIScriptError::warningFlag, "deprecatedDirective",
- params, ArrayLength(params));
mFrameSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
return mFrameSrc;
}
+ // if we have a worker-src, cache it so we can discard child-src for workers
+ if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE)) {
+ mWorkerSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
+ return mWorkerSrc;
+ }
+
+ // if we have a script-src, cache it as a fallback for worker-src
+ // in case child-src is not present
+ if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE)) {
+ mScriptSrc = new nsCSPScriptSrcDirective(CSP_StringToCSPDirective(mCurToken));
+ return mScriptSrc;
+ }
+
if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
return new nsRequireSRIForDirective(CSP_StringToCSPDirective(mCurToken));
}
@@ -1290,9 +1308,22 @@ nsCSPParser::policy()
directive();
}
- if (mChildSrc && !mFrameSrc) {
- // if we have a child-src, it handles frame-src too, unless frame-src is set
- mChildSrc->setHandleFrameSrc();
+ if (mChildSrc) {
+ if (!mFrameSrc) {
+ // if frame-src is specified explicitly for that policy than child-src should
+ // not restrict frames; if not, than child-src needs to restrict frames.
+ mChildSrc->setRestrictFrames();
+ }
+ if (!mWorkerSrc) {
+ // if worker-src is specified explicitly for that policy than child-src should
+ // not restrict workers; if not, than child-src needs to restrict workers.
+ mChildSrc->setRestrictWorkers();
+ }
+ }
+ // if script-src is specified, but not worker-src and also no child-src, then
+ // script-src has to govern workers.
+ if (mScriptSrc && !mWorkerSrc && !mChildSrc) {
+ mScriptSrc->setRestrictWorkers();
}
return mPolicy;
diff --git a/dom/security/nsCSPParser.h b/dom/security/nsCSPParser.h
index 1bfc56c65..d500a1c18 100644
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -243,14 +243,17 @@ class nsCSPParser {
bool mStrictDynamic; // false, if 'strict-dynamic' is not defined
nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate()
- // cache variables for child-src and frame-src directive handling.
- // frame-src is deprecated in favor of child-src, however if we
- // see a frame-src directive, it takes precedence for frames and iframes.
- // At the end of parsing, if we have a child-src directive, we need to
- // decide whether it will handle frames, or if there is a frame-src we
- // should honor instead.
- nsCSPChildSrcDirective* mChildSrc;
- nsCSPDirective* mFrameSrc;
+ // cache variables for child-src, frame-src and worker-src handling;
+ // in CSP 3 child-src is deprecated. For backwards compatibility
+ // child-src needs to restrict:
+ // (*) frames, in case frame-src is not expicitly specified
+ // (*) workers, in case worker-src is not expicitly specified
+ // If neither worker-src, nor child-src is present, then script-src
+ // needs to govern workers.
+ nsCSPChildSrcDirective* mChildSrc;
+ nsCSPDirective* mFrameSrc;
+ nsCSPDirective* mWorkerSrc;
+ nsCSPScriptSrcDirective* mScriptSrc;
// cache variable to let nsCSPHostSrc know that it's within
// the frame-ancestors directive.
diff --git a/dom/security/nsCSPUtils.cpp b/dom/security/nsCSPUtils.cpp
index a5f683b01..49832f8f4 100644
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -230,7 +230,7 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType)
case nsIContentPolicy::TYPE_INTERNAL_WORKER:
case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
- return nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE;
+ return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE;
case nsIContentPolicy::TYPE_SUBDOCUMENT:
return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
@@ -1184,6 +1184,11 @@ nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const
outCSP.mSandbox.Value() = mozilla::Move(srcs);
return;
+ case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE:
+ outCSP.mWorker_src.Construct();
+ outCSP.mWorker_src.Value() = mozilla::Move(srcs);
+ return;
+
// REFERRER_DIRECTIVE and REQUIRE_SRI_FOR are handled in nsCSPPolicy::toDomCSPStruct()
default:
@@ -1236,7 +1241,8 @@ bool nsCSPDirective::equals(CSPDirective aDirective) const
nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
: nsCSPDirective(aDirective)
- , mHandleFrameSrc(false)
+ , mRestrictFrames(false)
+ , mRestrictWorkers(false)
{
}
@@ -1244,30 +1250,58 @@ nsCSPChildSrcDirective::~nsCSPChildSrcDirective()
{
}
-void nsCSPChildSrcDirective::setHandleFrameSrc()
-{
- mHandleFrameSrc = true;
-}
-
bool nsCSPChildSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const
{
if (aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
- return mHandleFrameSrc;
+ return mRestrictFrames;
}
-
- return (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER
- || aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER
- || aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
- );
+ if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
+ aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER ||
+ aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) {
+ return mRestrictWorkers;
+ }
+ return false;
}
bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const
{
if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
- return mHandleFrameSrc;
+ return mRestrictFrames;
+ }
+ if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
+ return mRestrictWorkers;
}
+ return (mDirective == aDirective);
+}
- return (aDirective == nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE);
+/* =============== nsCSPScriptSrcDirective ============= */
+
+nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective)
+ : nsCSPDirective(aDirective)
+ , mRestrictWorkers(false)
+{
+}
+
+nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective()
+{
+}
+
+bool nsCSPScriptSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const
+{
+ if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
+ aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER ||
+ aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) {
+ return mRestrictWorkers;
+ }
+ return mDirective == CSP_ContentTypeToDirective(aContentType);
+}
+
+bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const
+{
+ if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
+ return mRestrictWorkers;
+ }
+ return (mDirective == aDirective);
}
/* =============== nsBlockAllMixedContentDirective ============= */
diff --git a/dom/security/nsCSPUtils.h b/dom/security/nsCSPUtils.h
index cfbe83256..91096712a 100644
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -93,7 +93,8 @@ static const char* CSPStrDirectives[] = {
"child-src", // CHILD_SRC_DIRECTIVE
"block-all-mixed-content", // BLOCK_ALL_MIXED_CONTENT
"require-sri-for", // REQUIRE_SRI_FOR
- "sandbox" // SANDBOX_DIRECTIVE
+ "sandbox", // SANDBOX_DIRECTIVE
+ "worker-src" // WORKER_SRC_DIRECTIVE
};
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
@@ -445,7 +446,7 @@ class nsCSPDirective {
bool visitSrcs(nsCSPSrcVisitor* aVisitor) const;
- private:
+ protected:
CSPDirective mDirective;
nsTArray<nsCSPBaseSrc*> mSrcs;
};
@@ -453,26 +454,52 @@ class nsCSPDirective {
/* =============== nsCSPChildSrcDirective ============= */
/*
- * In CSP 2, the child-src directive covers both workers and
- * subdocuments (i.e., frames and iframes). Workers were removed
- * from script-src, but frames can be controlled by either child-src
- * or frame-src directives, so child-src needs to know whether it should
- * also restrict frames. When both are present the frame-src directive
- * takes precedent.
+ * In CSP 3 child-src is deprecated. For backwards compatibility
+ * child-src needs to restrict:
+ * (*) frames, in case frame-src is not expicitly specified
+ * (*) workers, in case worker-src is not expicitly specified
*/
class nsCSPChildSrcDirective : public nsCSPDirective {
public:
explicit nsCSPChildSrcDirective(CSPDirective aDirective);
virtual ~nsCSPChildSrcDirective();
- void setHandleFrameSrc();
+ void setRestrictFrames()
+ { mRestrictFrames = true; }
+
+ void setRestrictWorkers()
+ { mRestrictWorkers = true; }
+
+ virtual bool restrictsContentType(nsContentPolicyType aContentType) const;
+
+ virtual bool equals(CSPDirective aDirective) const;
+
+ private:
+ bool mRestrictFrames;
+ bool mRestrictWorkers;
+};
+
+/* =============== nsCSPScriptSrcDirective ============= */
+
+/*
+ * In CSP 3 worker-src restricts workers, for backwards compatibily
+ * script-src has to restrict workers as the ultimate fallback if
+ * neither worker-src nor child-src is present in a CSP.
+ */
+class nsCSPScriptSrcDirective : public nsCSPDirective {
+ public:
+ explicit nsCSPScriptSrcDirective(CSPDirective aDirective);
+ virtual ~nsCSPScriptSrcDirective();
+
+ void setRestrictWorkers()
+ { mRestrictWorkers = true; }
virtual bool restrictsContentType(nsContentPolicyType aContentType) const;
virtual bool equals(CSPDirective aDirective) const;
private:
- bool mHandleFrameSrc;
+ bool mRestrictWorkers;
};
/* =============== nsBlockAllMixedContentDirective === */
diff --git a/dom/security/test/csp/file_frame_src.js b/dom/security/test/csp/file_frame_src.js
new file mode 100644
index 000000000..8e81f0743
--- /dev/null
+++ b/dom/security/test/csp/file_frame_src.js
@@ -0,0 +1,14 @@
+let testframe = document.getElementById("testframe");
+testframe.onload = function() {
+ parent.postMessage({
+ result: "frame-allowed",
+ href: document.location.href,
+ }, "*");
+}
+testframe.onerror = function() {
+ parent.postMessage({
+ result: "frame-blocked",
+ href: document.location.href,
+ }, "*");
+}
+testframe.src = "file_frame_src_inner.html"
diff --git a/dom/security/test/csp/file_frame_src_child_governs.html b/dom/security/test/csp/file_frame_src_child_governs.html
new file mode 100644
index 000000000..a51cb75be
--- /dev/null
+++ b/dom/security/test/csp/file_frame_src_child_governs.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="child-src https://example.com">";
+</head>
+<body>
+<iframe id="testframe"></iframe>
+<script type="text/javascript" src="file_frame_src.js"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_frame_src_frame_governs.html b/dom/security/test/csp/file_frame_src_frame_governs.html
new file mode 100644
index 000000000..2c5d5857f
--- /dev/null
+++ b/dom/security/test/csp/file_frame_src_frame_governs.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="frame-src https://example.com; child-src 'none'">";
+</head>
+<body>
+<iframe id="testframe"></iframe>
+<script type="text/javascript" src="file_frame_src.js"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_frame_src_inner.html b/dom/security/test/csp/file_frame_src_inner.html
new file mode 100644
index 000000000..4a2fc6095
--- /dev/null
+++ b/dom/security/test/csp/file_frame_src_inner.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+dummy iframe
+</body>
+</html>
diff --git a/dom/security/test/csp/file_spawn_service_worker.js b/dom/security/test/csp/file_spawn_service_worker.js
new file mode 100644
index 000000000..b262fa10a
--- /dev/null
+++ b/dom/security/test/csp/file_spawn_service_worker.js
@@ -0,0 +1 @@
+// dummy file
diff --git a/dom/security/test/csp/file_spawn_shared_worker.js b/dom/security/test/csp/file_spawn_shared_worker.js
new file mode 100644
index 000000000..00063bc5c
--- /dev/null
+++ b/dom/security/test/csp/file_spawn_shared_worker.js
@@ -0,0 +1,7 @@
+onconnect = function(e) {
+ var port = e.ports[0];
+ port.addEventListener("message", function(e) {
+ port.postMessage("shared worker is executing");
+ });
+ port.start();
+}
diff --git a/dom/security/test/csp/file_spawn_worker.js b/dom/security/test/csp/file_spawn_worker.js
new file mode 100644
index 000000000..acde7408c
--- /dev/null
+++ b/dom/security/test/csp/file_spawn_worker.js
@@ -0,0 +1 @@
+postMessage("worker is executing");
diff --git a/dom/security/test/csp/file_worker_src.js b/dom/security/test/csp/file_worker_src.js
new file mode 100644
index 000000000..ad3ade6a6
--- /dev/null
+++ b/dom/security/test/csp/file_worker_src.js
@@ -0,0 +1,52 @@
+
+let myWorker = new Worker("file_spawn_worker.js");
+myWorker.onmessage = function(event) {
+ parent.postMessage({
+ result: "worker-allowed",
+ href: document.location.href,
+ }, "*");
+}
+myWorker.onerror = function(event) {
+ parent.postMessage({
+ result: "worker-blocked",
+ href: document.location.href,
+ }, "*");
+}
+
+// --------------------------------------------
+
+var mySharedWorker = new SharedWorker('file_spawn_shared_worker.js');
+mySharedWorker.port.onmessage = function(ev) {
+ parent.postMessage({
+ result: "shared-worker-allowed",
+ href: document.location.href,
+ }, "*");
+}
+mySharedWorker.onerror = function(evt) {
+ evt.preventDefault();
+ parent.postMessage({
+ result: "shared-worker-blocked",
+ href: document.location.href,
+ }, "*");
+}
+mySharedWorker.port.start();
+mySharedWorker.port.postMessage('foo');
+
+// --------------------------------------------
+
+navigator.serviceWorker.register('file_spawn_service_worker.js')
+.then(function(reg) {
+ // registration worked
+ reg.unregister().then(function() {
+ parent.postMessage({
+ result: "service-worker-allowed",
+ href: document.location.href,
+ }, "*");
+ });
+}).catch(function(error) {
+ // registration failed
+ parent.postMessage({
+ result: "service-worker-blocked",
+ href: document.location.href,
+ }, "*");
+});
diff --git a/dom/security/test/csp/file_worker_src_child_governs.html b/dom/security/test/csp/file_worker_src_child_governs.html
new file mode 100644
index 000000000..ca8a683aa
--- /dev/null
+++ b/dom/security/test/csp/file_worker_src_child_governs.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="child-src https://example.com; script-src 'nonce-foo'">";
+</head>
+<body>
+<script type="text/javascript" src="file_worker_src.js" nonce="foo"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_worker_src_script_governs.html b/dom/security/test/csp/file_worker_src_script_governs.html
new file mode 100644
index 000000000..0385fee57
--- /dev/null
+++ b/dom/security/test/csp/file_worker_src_script_governs.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="script-src 'nonce-foo' https://example.com">";
+</head>
+<body>
+<script type="text/javascript" src="file_worker_src.js" nonce="foo"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_worker_src_worker_governs.html b/dom/security/test/csp/file_worker_src_worker_governs.html
new file mode 100644
index 000000000..93c8f6122
--- /dev/null
+++ b/dom/security/test/csp/file_worker_src_worker_governs.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="worker-src https://example.com; child-src 'none'; script-src 'nonce-foo'">";
+</head>
+<body>
+<script type="text/javascript" src="file_worker_src.js" nonce="foo"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini
index 2102cbe70..ca5c2c6ea 100644
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -316,3 +316,18 @@ support-files =
[test_punycode_host_src.html]
[test_websocket_self.html]
skip-if = toolkit == 'android'
+[test_worker_src.html]
+support-files =
+ file_worker_src_worker_governs.html
+ file_worker_src_child_governs.html
+ file_worker_src_script_governs.html
+ file_worker_src.js
+ file_spawn_worker.js
+ file_spawn_shared_worker.js
+ file_spawn_service_worker.js
+[test_frame_src.html]
+support-files =
+ file_frame_src_frame_governs.html
+ file_frame_src_child_governs.html
+ file_frame_src.js
+ file_frame_src_inner.html
diff --git a/dom/security/test/csp/test_child-src_worker.html b/dom/security/test/csp/test_child-src_worker.html
index 7dcbd03f6..ea9e7b28e 100644
--- a/dom/security/test/csp/test_child-src_worker.html
+++ b/dom/security/test/csp/test_child-src_worker.html
@@ -83,19 +83,19 @@
id: "script-src-worker",
file: WORKER_TEST_FILE,
result : "blocked",
- policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
+ policy : "default-src 'none'; script-src https://www.example.org 'unsafe-inline'"
},
'script-src-service_worker': {
id: "script-src-service_worker",
file: SERVICE_WORKER_TEST_FILE,
result : "blocked",
- policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
+ policy : "default-src 'none'; script-src https://www.example.org 'unsafe-inline'"
},
'script-src-self-shared_worker': {
id: "script-src-self-shared_worker",
file: SHARED_WORKER_TEST_FILE,
result : "blocked",
- policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
+ policy : "default-src 'none'; script-src https://www.example.org 'unsafe-inline'"
},
};
diff --git a/dom/security/test/csp/test_frame_src.html b/dom/security/test/csp/test_frame_src.html
new file mode 100644
index 000000000..07de90cfa
--- /dev/null
+++ b/dom/security/test/csp/test_frame_src.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1302667 - Test frame-src</title>
+ <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">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We load a page inlcuding a frame a CSP of:
+ * >> frame-src https://example.com; child-src 'none'
+ * and make sure that frame-src governs frames correctly. In addition,
+ * we make sure that child-src is discarded in case frame-src is specified.
+ */
+
+const ORIGIN_1 = "https://example.com/tests/dom/security/test/csp/";
+const ORIGIN_2 = "https://test1.example.com/tests/dom/security/test/csp/";
+
+let TESTS = [
+ // frame-src tests
+ ORIGIN_1 + "file_frame_src_frame_governs.html",
+ ORIGIN_2 + "file_frame_src_frame_governs.html",
+ // child-src tests
+ ORIGIN_1 + "file_frame_src_child_governs.html",
+ ORIGIN_2 + "file_frame_src_child_governs.html",
+];
+
+let testIndex = 0;
+
+function checkFinish() {
+ if (testIndex >= TESTS.length) {
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+ return;
+ }
+ runNextTest();
+}
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ let href = event.data.href;
+ let result = event.data.result;
+
+ if (href.startsWith("https://example.com")) {
+ if (result == "frame-allowed") {
+ ok(true, "allowing frame from https://example.com (" + result + ")");
+ }
+ else {
+ ok(false, "blocking frame from https://example.com (" + result + ")");
+ }
+ }
+ else if (href.startsWith("https://test1.example.com")) {
+ if (result == "frame-blocked") {
+ ok(true, "blocking frame from https://test1.example.com (" + result + ")");
+ }
+ else {
+ ok(false, "allowing frame from https://test1.example.com (" + result + ")");
+ }
+ }
+ else {
+ // sanity check, we should never enter that branch, bust just in case...
+ ok(false, "unexpected result: " + result);
+ }
+ checkFinish();
+}
+
+function runNextTest() {
+ document.getElementById("testframe").src = TESTS[testIndex];
+ testIndex++;
+}
+
+// fire up the tests
+runNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_worker_src.html b/dom/security/test/csp/test_worker_src.html
new file mode 100644
index 000000000..3f2b44c9f
--- /dev/null
+++ b/dom/security/test/csp/test_worker_src.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1302667 - Test worker-src</title>
+ <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="worker-testframe"></iframe>
+<iframe style="width:100%;" id="child-testframe"></iframe>
+<iframe style="width:100%;" id="script-testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We load a page inlcuding a worker, a shared worker as well as a
+ * service worker with a CSP of:
+ * >> worker-src https://example.com; child-src 'none'; script-src 'nonce-foo'
+ * and make sure that worker-src governs these three kinds of workers correctly.
+ * In addition, we make sure that child-src as well as script-src is discarded
+ * in case worker-src is specified. Ideally we would use "script-src 'none'" but
+ * we have to whitelist the actual script that spawns the workers, hence the nonce.
+ */
+
+let testRuns = 0;
+let messageCounter = 0;
+let numberSubTests = 9; // 3 workers * 3 frames = 9
+
+function checkFinish() {
+ messageCounter = 0;
+ if (testRuns == 0) {
+ testRuns++;
+ runTests("https://test1.example.com/tests/dom/security/test/csp/")
+ return;
+ }
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ let href = event.data.href;
+ let result = event.data.result;
+
+ if (href.startsWith("https://example.com")) {
+ if (result == "worker-allowed" ||
+ result == "shared-worker-allowed" ||
+ result == "service-worker-allowed") {
+ ok(true, "allowing worker from https://example.com (" + result + ")");
+ }
+ else {
+ ok(false, "blocking worker from https://example.com (" + result + ")");
+ }
+ }
+ else if (href.startsWith("https://test1.example.com")) {
+ if (result == "worker-blocked" ||
+ result == "shared-worker-blocked" ||
+ result == "service-worker-blocked") {
+ ok(true, "blocking worker from https://test1.example.com (" + result + ")");
+ }
+ else {
+ ok(false, "allowing worker from https://test1.example.com (" + result + ")");
+ }
+ }
+ else {
+ // sanity check, we should never enter that branch, bust just in case...
+ ok(false, "unexpected result: " + result);
+ }
+ messageCounter++;
+ if (messageCounter < numberSubTests) {
+ return;
+ }
+ checkFinish();
+}
+
+function runTests(aPath) {
+ document.getElementById("worker-testframe").src = aPath + "file_worker_src_worker_governs.html";
+ document.getElementById("child-testframe").src = aPath + "file_worker_src_child_governs.html";
+ document.getElementById("script-testframe").src = aPath + "file_worker_src_script_governs.html";
+}
+
+SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+]}, function() {
+ runTests("https://example.com/tests/dom/security/test/csp/");
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/gtest/TestCSPParser.cpp b/dom/security/test/gtest/TestCSPParser.cpp
index 8d168d81c..893e02db5 100644
--- a/dom/security/test/gtest/TestCSPParser.cpp
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -240,6 +240,10 @@ TEST(CSPParser, Directives)
"script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' https:" },
{ "default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https: ",
"default-src 'sha256-siVR8' 'unsafe-inline' https:" },
+ { "worker-src https://example.com",
+ "worker-src https://example.com" },
+ { "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com",
+ "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com" },
};
uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
diff --git a/dom/webidl/CSPDictionaries.webidl b/dom/webidl/CSPDictionaries.webidl
index 54008f13a..f8de1c9ad 100644
--- a/dom/webidl/CSPDictionaries.webidl
+++ b/dom/webidl/CSPDictionaries.webidl
@@ -30,6 +30,7 @@ dictionary CSP {
sequence<DOMString> block-all-mixed-content;
sequence<DOMString> require-sri-for;
sequence<DOMString> sandbox;
+ sequence<DOMString> worker-src;
};
dictionary CSPPolicies {