diff --git a/dom/interfaces/security/nsIContentSecurityPolicy.idl b/dom/interfaces/security/nsIContentSecurityPolicy.idl index f374226411..191ddb6e4b 100644 --- a/dom/interfaces/security/nsIContentSecurityPolicy.idl +++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl @@ -62,6 +62,8 @@ interface nsIContentSecurityPolicy : nsISerializable const unsigned short WORKER_SRC_DIRECTIVE = 22; const unsigned short SCRIPT_SRC_ELEM_DIRECTIVE = 23; const unsigned short SCRIPT_SRC_ATTR_DIRECTIVE = 24; + const unsigned short STYLE_SRC_ELEM_DIRECTIVE = 25; + const unsigned short STYLE_SRC_ATTR_DIRECTIVE = 26; /** * Accessor method for a read-only string version of the policy at a given diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 0569ddcb57..60cf33aeac 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -569,9 +569,9 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, if (aDirective != SCRIPT_SRC_ELEM_DIRECTIVE && aDirective != SCRIPT_SRC_ATTR_DIRECTIVE && - aDirective != STYLE_SRC_DIRECTIVE) { - MOZ_ASSERT(false, - "can only allow inline for script-src-(attr/elem) or style"); + aDirective != STYLE_SRC_ELEM_DIRECTIVE && + aDirective != STYLE_SRC_ATTR_DIRECTIVE) { + MOZ_ASSERT(false, "can only allow inline for (script/style)-src-(attr/elem) or style"); return NS_OK; } diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp index ddc45e2a8a..85bee16527 100644 --- a/dom/security/nsCSPParser.cpp +++ b/dom/security/nsCSPParser.cpp @@ -115,6 +115,7 @@ nsCSPParser::nsCSPParser(cspTokens& aTokens, , mFrameSrc(nullptr) , mWorkerSrc(nullptr) , mScriptSrc(nullptr) + , mStyleSrc(nullptr) , mParsingFrameAncestorsDir(false) , mTokens(aTokens) , mSelfURI(aSelfURI) @@ -1087,6 +1088,13 @@ nsCSPParser::directiveName() mScriptSrc = new nsCSPScriptSrcDirective(CSP_StringToCSPDirective(mCurToken)); return mScriptSrc; } + + // If we have a style-src, cache it as a fallback for style-src-elem and + // style-src-attr. + if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE)) { + mStyleSrc = new nsCSPStyleSrcDirective(CSP_StringToCSPDirective(mCurToken)); + return mStyleSrc; + } if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) { return new nsRequireSRIForDirective(CSP_StringToCSPDirective(mCurToken)); @@ -1314,6 +1322,18 @@ nsCSPParser::policy() mScriptSrc->setRestrictScriptAttr(); } + // If style-src is specified and style-src-elem is not specified, then + // style-src serves as a fallback. + if (mStyleSrc && !mPolicy->hasDirective(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE)) { + mStyleSrc->setRestrictStyleElem(); + } + + // If style-src is specified and style-src-attr is not specified, then + // style-src serves as a fallback. + if (mStyleSrc && !mPolicy->hasDirective(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE)) { + mStyleSrc->setRestrictStyleAttr(); + } + return mPolicy; } diff --git a/dom/security/nsCSPParser.h b/dom/security/nsCSPParser.h index 1ba4179809..56d1c39fcc 100644 --- a/dom/security/nsCSPParser.h +++ b/dom/security/nsCSPParser.h @@ -255,6 +255,7 @@ class nsCSPParser { nsCSPDirective* mFrameSrc; nsCSPDirective* mWorkerSrc; nsCSPScriptSrcDirective* mScriptSrc; + nsCSPStyleSrcDirective* mStyleSrc; // 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 93b0d4733c..70418e70c3 100644 --- a/dom/security/nsCSPUtils.cpp +++ b/dom/security/nsCSPUtils.cpp @@ -224,7 +224,7 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType) return nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE; case nsIContentPolicy::TYPE_STYLESHEET: - return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE; + return nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE; case nsIContentPolicy::TYPE_FONT: return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE; @@ -1313,12 +1313,9 @@ nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective) { } -nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() -{ -} +nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() = default; -bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const -{ +bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const { if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) { return mRestrictWorkers; } @@ -1331,6 +1328,27 @@ bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const return (mDirective == aDirective); } +/* =============== nsCSPStyleSrcDirective ============= */ + +nsCSPStyleSrcDirective::nsCSPStyleSrcDirective(CSPDirective aDirective) + : nsCSPDirective(aDirective) + , mRestrictStyleElem(false) + , mRestrictStyleAttr(false) +{ +} + +nsCSPStyleSrcDirective::~nsCSPStyleSrcDirective() = default; + +bool nsCSPStyleSrcDirective::equals(CSPDirective aDirective) const { + if (aDirective == nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) { + return mRestrictStyleElem; + } + if (aDirective == nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE) { + return mRestrictStyleAttr; + } + return (mDirective == aDirective); +} + /* =============== nsBlockAllMixedContentDirective ============= */ nsBlockAllMixedContentDirective::nsBlockAllMixedContentDirective(CSPDirective aDirective) diff --git a/dom/security/nsCSPUtils.h b/dom/security/nsCSPUtils.h index a2b2511e7b..4c0af92620 100644 --- a/dom/security/nsCSPUtils.h +++ b/dom/security/nsCSPUtils.h @@ -143,7 +143,9 @@ static const char* CSPStrDirectives[] = { "sandbox", // SANDBOX_DIRECTIVE "worker-src", // WORKER_SRC_DIRECTIVE "script-src-elem", // SCRIPT_SRC_ELEM_DIRECTIVE - "script-src-attr" // SCRIPT_SRC_ATTR_DIRECTIVE + "script-src-attr", // SCRIPT_SRC_ATTR_DIRECTIVE + "style-src-elem", // STYLE_SRC_ELEM_DIRECTIVE + "style-src-attr" // STYLE_SRC_ATTR_DIRECTIVE }; inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) @@ -551,6 +553,27 @@ class nsCSPScriptSrcDirective : public nsCSPDirective { bool mRestrictScriptAttr; }; +/* =============== nsCSPStyleSrcDirective ============= */ + +/* + * In CSP 3, style-src is used as a fallback for style-src-elem and + * style-src-attr in case they aren't defined. + */ +class nsCSPStyleSrcDirective : public nsCSPDirective { + public: + explicit nsCSPStyleSrcDirective(CSPDirective aDirective); + virtual ~nsCSPStyleSrcDirective(); + + void setRestrictStyleElem() { mRestrictStyleElem = true; } + void setRestrictStyleAttr() { mRestrictStyleAttr = true; } + + virtual bool equals(CSPDirective aDirective) const; + + private: + bool mRestrictStyleElem; + bool mRestrictStyleAttr; +}; + /* =============== nsBlockAllMixedContentDirective === */ class nsBlockAllMixedContentDirective : public nsCSPDirective { diff --git a/dom/security/test/csp/test_report_for_import.html b/dom/security/test/csp/test_report_for_import.html index be112d51c0..c8ced5a244 100644 --- a/dom/security/test/csp/test_report_for_import.html +++ b/dom/security/test/csp/test_report_for_import.html @@ -50,7 +50,7 @@ function checkResults(reportStr) { "http://mochi.test:8888/tests/dom/security/test/csp/test_report_for_import.html", "Incorrect referrer"); is(cspReport["violated-directive"], - "style-src http://mochi.test:8888", + "style-src-elem http://mochi.test:8888", "Incorrect violated-directive"); is(cspReport["original-policy"], "style-src http://mochi.test:8888; report-uri " + diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp index 55329cc6e8..1f5de4fb83 100644 --- a/layout/style/nsStyleUtil.cpp +++ b/layout/style/nsStyleUtil.cpp @@ -767,14 +767,16 @@ nsStyleUtil::CSPAllowsInlineStyle(nsIContent* aContent, return true; } + CSPDirective directive = nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE; // query the nonce nsAutoString nonce; if (aContent) { + directive = nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE; aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nonce, nonce); } bool allowInlineStyle = true; - rv = csp->GetAllowsInline(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE, + rv = csp->GetAllowsInline(directive, nonce, false, // aParserCreated only applies to scripts aStyleText, aLineNumber, aColumnNumber,