Skip to content

Commit

Permalink
Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specif…
Browse files Browse the repository at this point in the history
…ied (r=sstamm)

--HG--
extra : rebase_source : 119d3903ba156300f49f097efe2d18bbf8c8a6b5
  • Loading branch information
Christoph Kerschbaumer committed Apr 7, 2015
1 parent 4653f5d commit b2c8937
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 6 deletions.
7 changes: 7 additions & 0 deletions dom/locales/en-US/chrome/security/csp.properties
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ couldNotProcessUnknownDirective = Couldn't process unknown directive '%1$S'
# LOCALIZATION NOTE (ignoringUnknownOption):
# %1$S is the option that could not be understood
ignoringUnknownOption = Ignoring unknown option %1$S
# LOCALIZATION NOTE (ignoringDuplicateSrc):
# %1$S defines the duplicate src
ignoringDuplicateSrc = Ignoring duplicate source %1$S
# LOCALIZATION NOTE (ignoringSrcWithinScriptSrc):
# %1$S is the ignored src
# script-src is a directive name and should not be localized
ignoringSrcWithinScriptSrc = Ignoring "%1$S" within script-src: nonce-source or hash-source specified
# LOCALIZATION NOTE (reportURInotHttpsOrHttp2):
# %1$S is the ETLD of the report URI that is not HTTP or HTTPS
reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI.
Expand Down
43 changes: 40 additions & 3 deletions dom/security/nsCSPParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ nsCSPTokenizer::tokenizeCSPPolicy(const nsAString &aPolicyString,
nsCSPParser::nsCSPParser(cspTokens& aTokens,
nsIURI* aSelfURI,
uint64_t aInnerWindowID)
: mTokens(aTokens)
: mHasHashOrNonce(false)
, mUnsafeInlineKeywordSrc(nullptr)
, mTokens(aTokens)
, mSelfURI(aSelfURI)
, mInnerWindowID(aInnerWindowID)
{
Expand Down Expand Up @@ -570,8 +572,22 @@ nsCSPParser::keywordSource()
return CSP_CreateHostSrcFromURI(mSelfURI);
}

if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE) ||
CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
// make sure script-src only contains 'unsafe-inline' once;
// ignore duplicates and log warning
if (mUnsafeInlineKeywordSrc) {
const char16_t* params[] = { mCurToken.get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDuplicateSrc",
params, ArrayLength(params));
return nullptr;
}
// cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
// case that script-src directive also contains hash- or nonce-.
mUnsafeInlineKeywordSrc = new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
return mUnsafeInlineKeywordSrc;
}

if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
}
return nullptr;
Expand Down Expand Up @@ -676,6 +692,8 @@ nsCSPParser::nonceSource()
if (dashIndex < 0) {
return nullptr;
}
// cache if encountering hash or nonce to invalidate unsafe-inline
mHasHashOrNonce = true;
return new nsCSPNonceSrc(Substring(expr,
dashIndex + 1,
expr.Length() - dashIndex + 1));
Expand Down Expand Up @@ -708,6 +726,8 @@ nsCSPParser::hashSource()

for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) {
if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) {
// cache if encountering hash or nonce to invalidate unsafe-inline
mHasHashOrNonce = true;
return new nsCSPHashSrc(algo, hash);
}
}
Expand Down Expand Up @@ -1003,6 +1023,11 @@ nsCSPParser::directive()
return;
}

// make sure to reset cache variables when trying to invalidate unsafe-inline;
// unsafe-inline might not only appear in script-src, but also in default-src
mHasHashOrNonce = false;
mUnsafeInlineKeywordSrc = nullptr;

// Try to parse all the srcs by handing the array off to directiveValue
nsTArray<nsCSPBaseSrc*> srcs;
directiveValue(srcs);
Expand All @@ -1014,6 +1039,18 @@ nsCSPParser::directive()
srcs.AppendElement(keyword);
}

// if a hash or nonce is specified within script-src, then
// unsafe-inline should be ignored, see:
// http://www.w3.org/TR/CSP2/#directive-script-src
if (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) &&
mHasHashOrNonce && mUnsafeInlineKeywordSrc) {
mUnsafeInlineKeywordSrc->invalidate();
// log to the console that unsafe-inline will be ignored
const char16_t* params[] = { NS_LITERAL_STRING("'unsafe-inline'").get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptSrc",
params, ArrayLength(params));
}

// Add the newly created srcs to the directive and add the directive to the policy
cspDir->addSrcs(srcs);
mPolicy->addDirective(cspDir);
Expand Down
4 changes: 4 additions & 0 deletions dom/security/nsCSPParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,10 @@ class nsCSPParser {
nsString mCurToken;
nsTArray<nsString> mCurDir;

// cache variables to ignore unsafe-inline if hash or nonce is specified
bool mHasHashOrNonce; // false, if no hash or nonce is defined
nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate()

cspTokens mTokens;
nsIURI* mSelfURI;
nsCSPPolicy* mPolicy;
Expand Down
23 changes: 20 additions & 3 deletions dom/security/nsCSPUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,11 @@ nsCSPHostSrc::appendPath(const nsAString& aPath)
/* ===== nsCSPKeywordSrc ===================== */

nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
: mKeyword(aKeyword)
, mInvalidated(false)
{
NS_ASSERTION((aKeyword != CSP_SELF),
"'self' should have been replaced in the parser");
mKeyword = aKeyword;
}

nsCSPKeywordSrc::~nsCSPKeywordSrc()
Expand All @@ -553,8 +554,16 @@ nsCSPKeywordSrc::~nsCSPKeywordSrc()
bool
nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
{
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s",
CSP_EnumToKeyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get(),
mInvalidated ? "yes" : "false"));
// if unsafe-inline should be ignored, then bail early
if (mInvalidated) {
NS_ASSERTION(mKeyword == CSP_UNSAFE_INLINE,
"should only invalidate unsafe-inline within script-src");
return false;
}
return mKeyword == aKeyword;
}

Expand All @@ -564,6 +573,14 @@ nsCSPKeywordSrc::toString(nsAString& outStr) const
outStr.AppendASCII(CSP_EnumToKeyword(mKeyword));
}

void
nsCSPKeywordSrc::invalidate()
{
mInvalidated = true;
NS_ASSERTION(mInvalidated == CSP_UNSAFE_INLINE,
"invalidate 'unsafe-inline' only within script-src");
}

/* ===== nsCSPNonceSrc ==================== */

nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce)
Expand Down
3 changes: 3 additions & 0 deletions dom/security/nsCSPUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,12 @@ class nsCSPKeywordSrc : public nsCSPBaseSrc {

bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
void toString(nsAString& outStr) const;
void invalidate();

private:
CSPKeyword mKeyword;
// invalidate 'unsafe-inline' if nonce- or hash-source specified
bool mInvalidated;
};

/* =============== nsCSPNonceSource =========== */
Expand Down

0 comments on commit b2c8937

Please sign in to comment.