Bug 1529338 - Implement CSP 'style-src-elem' and 'style-src-attr' directives. r=freddyb,emilio,dveditz

Differential Revision: https://phabricator.services.mozilla.com/D151926
This commit is contained in:
Tom Schuster 2022-08-01 12:32:59 +00:00
parent 059ff10a60
commit 4b8ffda4e2
35 changed files with 118 additions and 146 deletions

View File

@ -64,6 +64,8 @@ interface nsIContentSecurityPolicy : nsISerializable
NAVIGATE_TO_DIRECTIVE = 21,
SCRIPT_SRC_ELEM_DIRECTIVE = 22,
SCRIPT_SRC_ATTR_DIRECTIVE = 23,
STYLE_SRC_ELEM_DIRECTIVE = 24,
STYLE_SRC_ATTR_DIRECTIVE = 25,
};
/**

View File

@ -201,11 +201,17 @@ bool nsCSPContext::permitsInternal(
permits = false;
}
// See the comment in nsCSPContext::GetAllowsInline.
// In CSP 3.0 the effective directive doesn't become the actually used
// directive in case of a fallback from e.g. script-src-elem to
// script-src or default-src.
// TODO(bug 1779369): Fix this for all directive types.
nsAutoString effectiveDirective(violatedDirective);
if ((StaticPrefs::security_csp_script_src_attr_elem_enabled() &&
(aDir == SCRIPT_SRC_ELEM_DIRECTIVE ||
aDir == SCRIPT_SRC_ATTR_DIRECTIVE))) {
(aDir == SCRIPT_SRC_ELEM_DIRECTIVE ||
aDir == SCRIPT_SRC_ATTR_DIRECTIVE)) ||
(StaticPrefs::security_csp_style_src_attr_elem_enabled() &&
(aDir == STYLE_SRC_ELEM_DIRECTIVE ||
aDir == STYLE_SRC_ATTR_DIRECTIVE))) {
effectiveDirective.AssignASCII(CSP_CSPDirectiveToString(aDir));
}
@ -584,9 +590,10 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
if (aDirective != SCRIPT_SRC_ELEM_DIRECTIVE &&
aDirective != SCRIPT_SRC_ATTR_DIRECTIVE &&
aDirective != STYLE_SRC_DIRECTIVE) {
aDirective != STYLE_SRC_ELEM_DIRECTIVE &&
aDirective != STYLE_SRC_ATTR_DIRECTIVE) {
MOZ_ASSERT(false,
"can only allow inline for script-src-(attr/elem) or style");
"can only allow inline for (script/style)-src-(attr/elem)");
return NS_OK;
}
@ -634,6 +641,7 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
bool reportSample = false;
mPolicies[i]->getDirectiveStringAndReportSampleForContentType(
aDirective, violatedDirective, &reportSample);
// In CSP 3.0 the effective directive doesn't become the actually used
// directive in case of a fallback from e.g. script-src-elem to
// script-src or default-src.
@ -641,7 +649,10 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
nsAutoString effectiveDirective(violatedDirective);
if ((StaticPrefs::security_csp_script_src_attr_elem_enabled() &&
(aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
aDirective == SCRIPT_SRC_ATTR_DIRECTIVE))) {
aDirective == SCRIPT_SRC_ATTR_DIRECTIVE)) ||
(StaticPrefs::security_csp_style_src_attr_elem_enabled() &&
(aDirective == STYLE_SRC_ELEM_DIRECTIVE ||
aDirective == STYLE_SRC_ATTR_DIRECTIVE))) {
effectiveDirective.AssignASCII(CSP_CSPDirectiveToString(aDirective));
}
@ -651,6 +662,7 @@ nsCSPContext::GetAllowsInline(CSPDirective aDirective, const nsAString& aNonce,
aLineNumber, aColumnNumber);
}
}
return NS_OK;
}
@ -1519,8 +1531,8 @@ class CSPReportSenderRunnable final : public Runnable {
}
// 4) fire violation event
// A frame-ancestors violation has occurred, but we should not dispatch the
// violation event to a potentially cross-origin ancestor.
// A frame-ancestors violation has occurred, but we should not dispatch
// the violation event to a potentially cross-origin ancestor.
if (!mViolatedDirective.EqualsLiteral("frame-ancestors")) {
mCSPContext->FireViolationEvent(mTriggeringElement, mCSPEventListener,
init);
@ -1869,9 +1881,9 @@ CSPReportRedirectSink::AsyncOnChannelRedirect(
nsresult rv = aOldChannel->Cancel(NS_ERROR_ABORT);
NS_ENSURE_SUCCESS(rv, rv);
// notify an observer that we have blocked the report POST due to a redirect,
// used in testing, do this async since we're in an async call now to begin
// with
// notify an observer that we have blocked the report POST due to a
// redirect, used in testing, do this async since we're in an async call now
// to begin with
nsCOMPtr<nsIURI> uri;
rv = aOldChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -50,6 +50,7 @@ nsCSPParser::nsCSPParser(policyTokens& aTokens, nsIURI* aSelfURI,
mFrameSrc(nullptr),
mWorkerSrc(nullptr),
mScriptSrc(nullptr),
mStyleSrc(nullptr),
mParsingFrameAncestorsDir(false),
mTokens(aTokens.Clone()),
mSelfURI(aSelfURI),
@ -859,9 +860,13 @@ nsCSPDirective* nsCSPParser::directiveName() {
}
// script-src-attr and script-scr-elem might have been disabled.
if ((directive == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE ||
directive == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) &&
!StaticPrefs::security_csp_script_src_attr_elem_enabled()) {
// Similarly style-src-{attr, elem}.
if (((directive == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE ||
directive == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) &&
!StaticPrefs::security_csp_script_src_attr_elem_enabled()) ||
((directive == nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE ||
directive == nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) &&
!StaticPrefs::security_csp_style_src_attr_elem_enabled())) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag,
"notSupportingDirective", params);
@ -941,6 +946,13 @@ nsCSPDirective* nsCSPParser::directiveName() {
return mScriptSrc;
}
// If we have a style-src, cache it as a fallback for style-src-elem and
// style-src-attr.
if (directive == nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) {
mStyleSrc = new nsCSPStyleSrcDirective(directive);
return mStyleSrc;
}
return new nsCSPDirective(directive);
}
@ -1155,6 +1167,20 @@ nsCSPPolicy* 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-attr-elem is not specified, then
// style-src serves as a fallback.
if (mStyleSrc && !mPolicy->hasDirective(
nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE)) {
mStyleSrc->setRestrictStyleAttr();
}
return mPolicy;
}

View File

@ -198,6 +198,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.

View File

@ -289,7 +289,7 @@ CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
case nsIContentPolicy::TYPE_STYLESHEET:
case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
return nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE;
case nsIContentPolicy::TYPE_FONT:
case nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD:
@ -1293,10 +1293,7 @@ bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const {
/* =============== nsCSPScriptSrcDirective ============= */
nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective)
: nsCSPDirective(aDirective),
mRestrictWorkers(false),
mRestrictScriptElem(false),
mRestrictScriptAttr(false) {}
: nsCSPDirective(aDirective) {}
nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() = default;
@ -1310,7 +1307,24 @@ bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const {
if (aDirective == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) {
return mRestrictScriptAttr;
}
return (mDirective == aDirective);
return mDirective == aDirective;
}
/* =============== nsCSPStyleSrcDirective ============= */
nsCSPStyleSrcDirective::nsCSPStyleSrcDirective(CSPDirective aDirective)
: nsCSPDirective(aDirective) {}
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 ============= */

View File

@ -91,6 +91,8 @@ static const char* CSPStrDirectives[] = {
"navigate-to", // NAVIGATE_TO_DIRECTIVE
"script-src-elem", // SCRIPT_SRC_ELEM_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) {
@ -515,12 +517,33 @@ class nsCSPScriptSrcDirective : public nsCSPDirective {
void setRestrictScriptElem() { mRestrictScriptElem = true; }
void setRestrictScriptAttr() { mRestrictScriptAttr = true; }
virtual bool equals(CSPDirective aDirective) const override;
bool equals(CSPDirective aDirective) const override;
private:
bool mRestrictWorkers;
bool mRestrictScriptElem;
bool mRestrictScriptAttr;
bool mRestrictWorkers = false;
bool mRestrictScriptElem = false;
bool mRestrictScriptAttr = false;
};
/* =============== nsCSPStyleSrcDirective ============= */
/*
* In CSP 3 style-src is use as a fallback for style-src-elem and
* style-src-attr.
*/
class nsCSPStyleSrcDirective : public nsCSPDirective {
public:
explicit nsCSPStyleSrcDirective(CSPDirective aDirective);
virtual ~nsCSPStyleSrcDirective();
void setRestrictStyleElem() { mRestrictStyleElem = true; }
void setRestrictStyleAttr() { mRestrictStyleAttr = true; }
bool equals(CSPDirective aDirective) const override;
private:
bool mRestrictStyleElem = false;
bool mRestrictStyleAttr = false;
};
/* =============== nsBlockAllMixedContentDirective === */

View File

@ -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",
"style-src-elem",
"Incorrect violated-directive");
is(cspReport["original-policy"], POLICY, "Incorrect original-policy");
is(cspReport["blocked-uri"],

View File

@ -306,9 +306,12 @@ bool nsStyleUtil::CSPAllowsInlineStyle(
return true;
}
nsIContentSecurityPolicy::CSPDirective directive =
nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE;
// query the nonce
nsAutoString nonce;
if (aElement && aElement->NodeInfo()->NameAtom() == nsGkAtoms::style) {
directive = nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE;
nsString* cspNonce =
static_cast<nsString*>(aElement->GetProperty(nsGkAtoms::nonce));
if (cspNonce) {
@ -317,11 +320,11 @@ bool nsStyleUtil::CSPAllowsInlineStyle(
}
bool allowInlineStyle = true;
rv = csp->GetAllowsInline(
nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE, nonce,
false, // aParserCreated only applies to scripts
aElement, nullptr, // nsICSPEventListener
aStyleText, aLineNumber, aColumnNumber, &allowInlineStyle);
rv = csp->GetAllowsInline(directive, nonce,
false, // aParserCreated only applies to scripts
aElement, nullptr, // nsICSPEventListener
aStyleText, aLineNumber, aColumnNumber,
&allowInlineStyle);
NS_ENSURE_SUCCESS(rv, false);
return allowInlineStyle;

View File

@ -12337,6 +12337,12 @@
value: @IS_NIGHTLY_BUILD@
mirror: always
# The style-src-attr and style-src-elem directive
- name: security.csp.style-src-attr-elem.enabled
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
# No way to enable on Android, Bug 1552602
- name: security.webauth.u2f
type: bool

View File

@ -1 +1 @@
prefs: [security.csp.script-src-attr-elem.enabled:true]
prefs: [security.csp.script-src-attr-elem.enabled:true, security.csp.style-src-attr-elem.enabled:true]

View File

@ -1,4 +0,0 @@
[combine-header-and-meta-policies.sub.html]
[Expecting logs: ["TEST COMPLETE", "violated-directive=img-src", "violated-directive=style-src-elem"\]]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-attr-allowed-src-blocked.html]
[Should apply the style attribute]
expected: FAIL

View File

@ -1,8 +0,0 @@
[style-src-attr-blocked-src-allowed.html]
expected: TIMEOUT
[The attribute style should not be applied]
expected: FAIL
[Should fire a security policy violation event]
expected: NOTRUN

View File

@ -1,8 +0,0 @@
[style-src-elem-allowed-attr-blocked.html]
expected: TIMEOUT
[The attribute style should not be applied and the inline style should be applied]
expected: FAIL
[Should fire a security policy violation for the attribute]
expected: NOTRUN

View File

@ -1,4 +0,0 @@
[style-src-elem-allowed-src-blocked.html]
[Inline style should be applied]
expected: FAIL

View File

@ -1,8 +0,0 @@
[style-src-elem-blocked-attr-allowed.html]
expected: TIMEOUT
[Should fire a security policy violation for the inline block]
expected: NOTRUN
[The inline style should not be applied and the attribute style should be applied]
expected: FAIL

View File

@ -1,8 +0,0 @@
[style-src-elem-blocked-src-allowed.html]
expected: TIMEOUT
[Should fire a security policy violation event]
expected: NOTRUN
[The inline style should not be applied]
expected: FAIL

View File

@ -1,3 +0,0 @@
[injected-inline-style-blocked.sub.html]
[Expecting logs: ["violated-directive=style-src-elem","violated-directive=style-src-elem","PASS"\]]
expected: FAIL

View File

@ -1,7 +1,4 @@
[inline-style-allowed-while-cloning-objects.sub.html]
[Test that violation report event was fired]
expected: FAIL
[inline-style-allowed-while-cloning-objects 18]
expected: FAIL

View File

@ -1,5 +0,0 @@
implementation-status: backlog
[inline-style-attribute-blocked.sub.html]
[Expecting logs: ["violated-directive=style-src-attr","PASS"\]]
expected: FAIL

View File

@ -1,6 +1,3 @@
[style-blocked.html]
[Violated directive is script-src-elem.]
expected: FAIL
[document.styleSheets should contain an item for the blocked CSS.]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-hash-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-imported-style-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-injected-inline-style-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-injected-stylesheet-blocked.sub.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,5 +0,0 @@
implementation-status: backlog
[style-src-inline-style-attribute-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-inline-style-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -4,6 +4,3 @@ implementation-status: backlog
[Test that paragraph remains unmodified and error events received.]
expected: NOTRUN
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-inline-style-nonce-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-none-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[style-src-stylesheet-nonce-blocked.html]
[Should fire a securitypolicyviolation event]
expected: FAIL

View File

@ -1,4 +0,0 @@
[stylehash-basic-blocked.sub.html]
[Expecting alerts: ["PASS: The 'p' element's text is green, which means the style was correctly applied.", "violated-directive=style-src-elem"\]]
expected: FAIL

View File

@ -1,4 +0,0 @@
[stylenonce-allowed.sub.html]
[Should fire securitypolicyviolation]
expected: FAIL

View File

@ -1,4 +0,0 @@
[stylenonce-blocked.sub.html]
[Should fire securitypolicyviolation]
expected: FAIL

View File

@ -1,5 +0,0 @@
implementation-status: backlog
[style_attribute_denied_wrong_hash.html]
[Test that the inline style attribute is blocked]
expected: FAIL