Bug 1406794 - Provide the CSP keywords in both UTF8 and UTF16 forms. r=ckerschb

This avoids the need for numerous 8-to-16-bit and 16-to-8-bit string
conversions.

The patch also introduces a higher-order macro, FOR_EACH_CSP_KEYWORD, which
defines all the stuff about the keywords in a single place and makes the code
nicer.

--HG--
extra : rebase_source : b0f655546aa397749bb18dc7d6d27fbc12fe8fca
This commit is contained in:
Nicholas Nethercote 2017-10-06 16:16:52 +11:00
Родитель 0b432c20bc
Коммит 159f6b5627
4 изменённых файлов: 82 добавлений и 61 удалений

Просмотреть файл

@ -552,7 +552,7 @@ nsCSPParser::keywordSource()
return nullptr; return nullptr;
} }
mStrictDynamic = true; mStrictDynamic = true;
return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken)); return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
} }
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) { if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
@ -571,7 +571,8 @@ nsCSPParser::keywordSource()
} }
// cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in // cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
// case that script-src directive also contains hash- or nonce-. // case that script-src directive also contains hash- or nonce-.
mUnsafeInlineKeywordSrc = new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken)); mUnsafeInlineKeywordSrc =
new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
return mUnsafeInlineKeywordSrc; return mUnsafeInlineKeywordSrc;
} }
@ -581,7 +582,7 @@ nsCSPParser::keywordSource()
if (doc) { if (doc) {
doc->SetHasUnsafeEvalCSP(true); doc->SetHasUnsafeEvalCSP(true);
} }
return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken)); return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
} }
return nullptr; return nullptr;
} }
@ -665,7 +666,8 @@ nsCSPParser::nonceSource()
NS_ConvertUTF16toUTF8(mCurValue).get())); NS_ConvertUTF16toUTF8(mCurValue).get()));
// Check if mCurToken begins with "'nonce-" and ends with "'" // Check if mCurToken begins with "'nonce-" and ends with "'"
if (!StringBeginsWith(mCurToken, NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONCE)), if (!StringBeginsWith(mCurToken,
nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE)),
nsASCIICaseInsensitiveStringComparator()) || nsASCIICaseInsensitiveStringComparator()) ||
mCurToken.Last() != SINGLEQUOTE) { mCurToken.Last() != SINGLEQUOTE) {
return nullptr; return nullptr;
@ -861,8 +863,7 @@ nsCSPParser::sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs)
} }
// Otherwise, we ignore 'none' and report a warning // Otherwise, we ignore 'none' and report a warning
else { else {
NS_ConvertUTF8toUTF16 unicodeNone(CSP_EnumToKeyword(CSP_NONE)); const char16_t* params[] = { CSP_EnumToUTF16Keyword(CSP_NONE) };
const char16_t* params[] = { unicodeNone.get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption", logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
params, ArrayLength(params)); params, ArrayLength(params));
} }
@ -1252,10 +1253,10 @@ nsCSPParser::directive()
// Even though we invalidate all of the srcs internally, we don't want to log // Even though we invalidate all of the srcs internally, we don't want to log
// messages for the srcs: (1) strict-dynamic, (2) unsafe-inline, // messages for the srcs: (1) strict-dynamic, (2) unsafe-inline,
// (3) nonces, and (4) hashes // (3) nonces, and (4) hashes
if (!srcStr.EqualsASCII(CSP_EnumToKeyword(CSP_STRICT_DYNAMIC)) && if (!srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_STRICT_DYNAMIC)) &&
!srcStr.EqualsASCII(CSP_EnumToKeyword(CSP_UNSAFE_EVAL)) && !srcStr.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_UNSAFE_EVAL)) &&
!StringBeginsWith(NS_ConvertUTF16toUTF8(srcStr), NS_LITERAL_CSTRING("'nonce-")) && !StringBeginsWith(srcStr, nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE))) &&
!StringBeginsWith(NS_ConvertUTF16toUTF8(srcStr), NS_LITERAL_CSTRING("'sha"))) !StringBeginsWith(srcStr, NS_LITERAL_STRING("'sha")))
{ {
const char16_t* params[] = { srcStr.get() }; const char16_t* params[] = { srcStr.get() };
logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcForStrictDynamic", logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcForStrictDynamic",

Просмотреть файл

@ -322,7 +322,7 @@ CSP_IsDirective(const nsAString& aValue, CSPDirective aDir)
bool bool
CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey) CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey)
{ {
return aValue.LowerCaseEqualsASCII(CSP_EnumToKeyword(aKey)); return aValue.LowerCaseEqualsASCII(CSP_EnumToUTF8Keyword(aKey));
} }
bool bool
@ -331,14 +331,10 @@ CSP_IsQuotelessKeyword(const nsAString& aKey)
nsString lowerKey = PromiseFlatString(aKey); nsString lowerKey = PromiseFlatString(aKey);
ToLowerCase(lowerKey); ToLowerCase(lowerKey);
static_assert(CSP_LAST_KEYWORD_VALUE ==
(sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0])),
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
nsAutoString keyword; nsAutoString keyword;
for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) { for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
// skipping the leading ' and trimming the trailing ' // skipping the leading ' and trimming the trailing '
keyword.AssignASCII(CSPStrKeywords[i] + 1); keyword.AssignASCII(gCSPUTF8Keywords[i] + 1);
keyword.Trim("'", false, true); keyword.Trim("'", false, true);
if (lowerKey.Equals(keyword)) { if (lowerKey.Equals(keyword)) {
return true; return true;
@ -482,7 +478,7 @@ nsCSPBaseSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
bool aParserCreated) const bool aParserCreated) const
{ {
CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s", CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
aKeyword == CSP_HASH ? "hash" : CSP_EnumToKeyword(aKeyword), aKeyword == CSP_HASH ? "hash" : CSP_EnumToUTF8Keyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get())); NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
return false; return false;
} }
@ -827,7 +823,7 @@ nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
bool aParserCreated) const bool aParserCreated) const
{ {
CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s", CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s",
CSP_EnumToKeyword(aKeyword), CSP_EnumToUTF8Keyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get(), NS_ConvertUTF16toUTF8(aHashOrNonce).get(),
mInvalidated ? "yes" : "false")); mInvalidated ? "yes" : "false"));
@ -853,7 +849,7 @@ nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const
void void
nsCSPKeywordSrc::toString(nsAString& outStr) const nsCSPKeywordSrc::toString(nsAString& outStr) const
{ {
outStr.AppendASCII(CSP_EnumToKeyword(mKeyword)); outStr.Append(CSP_EnumToUTF16Keyword(mKeyword));
} }
/* ===== nsCSPNonceSrc ==================== */ /* ===== nsCSPNonceSrc ==================== */
@ -886,7 +882,8 @@ nsCSPNonceSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
bool aParserCreated) const bool aParserCreated) const
{ {
CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s", CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get())); CSP_EnumToUTF8Keyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
if (aKeyword != CSP_NONCE) { if (aKeyword != CSP_NONCE) {
return false; return false;
@ -904,7 +901,7 @@ nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const
void void
nsCSPNonceSrc::toString(nsAString& outStr) const nsCSPNonceSrc::toString(nsAString& outStr) const
{ {
outStr.AppendASCII(CSP_EnumToKeyword(CSP_NONCE)); outStr.Append(CSP_EnumToUTF16Keyword(CSP_NONCE));
outStr.Append(mNonce); outStr.Append(mNonce);
outStr.AppendASCII("'"); outStr.AppendASCII("'");
} }
@ -928,7 +925,8 @@ nsCSPHashSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
bool aParserCreated) const bool aParserCreated) const
{ {
CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s", CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get())); CSP_EnumToUTF8Keyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
if (aKeyword != CSP_HASH) { if (aKeyword != CSP_HASH) {
return false; return false;
@ -1061,7 +1059,8 @@ nsCSPDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
bool aParserCreated) const bool aParserCreated) const
{ {
CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s", CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s",
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get())); CSP_EnumToUTF8Keyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
for (uint32_t i = 0; i < mSrcs.Length(); i++) { for (uint32_t i = 0; i < mSrcs.Length(); i++) {
if (mSrcs[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) { if (mSrcs[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
@ -1450,7 +1449,8 @@ nsCSPPolicy::allows(nsContentPolicyType aContentType,
bool aParserCreated) const bool aParserCreated) const
{ {
CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s", CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get())); CSP_EnumToUTF8Keyword(aKeyword),
NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
nsCSPDirective* defaultDir = nullptr; nsCSPDirective* defaultDir = nullptr;

Просмотреть файл

@ -11,6 +11,7 @@
#include "nsIContentPolicy.h" #include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h" #include "nsIContentSecurityPolicy.h"
#include "nsIURI.h" #include "nsIURI.h"
#include "nsLiteralString.h"
#include "nsString.h" #include "nsString.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "nsUnicharUtils.h" #include "nsUnicharUtils.h"
@ -116,61 +117,81 @@ inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
return nsIContentSecurityPolicy::NO_DIRECTIVE; return nsIContentSecurityPolicy::NO_DIRECTIVE;
} }
// Please add any new enum items not only to CSPKeyword, but also add #define FOR_EACH_CSP_KEYWORD(macro) \
// a string version for every enum >> using the same index << to macro(CSP_SELF, "'self'") \
// CSPStrKeywords underneath. macro(CSP_UNSAFE_INLINE, "'unsafe-inline'") \
macro(CSP_UNSAFE_EVAL, "'unsafe-eval'") \
macro(CSP_NONE, "'none'") \
macro(CSP_NONCE, "'nonce-") \
macro(CSP_REQUIRE_SRI_FOR, "require-sri-for") \
macro(CSP_STRICT_DYNAMIC, "'strict-dynamic'")
enum CSPKeyword { enum CSPKeyword {
CSP_SELF = 0, #define KEYWORD_ENUM(id_, string_) id_,
CSP_UNSAFE_INLINE, FOR_EACH_CSP_KEYWORD(KEYWORD_ENUM)
CSP_UNSAFE_EVAL, #undef KEYWORD_ENUM
CSP_NONE,
CSP_NONCE,
CSP_REQUIRE_SRI_FOR,
CSP_STRICT_DYNAMIC,
// CSP_LAST_KEYWORD_VALUE always needs to be the last element in the enum // CSP_LAST_KEYWORD_VALUE always needs to be the last element in the enum
// because we use it to calculate the size for the char* array. // because we use it to calculate the size for the char* array.
CSP_LAST_KEYWORD_VALUE, CSP_LAST_KEYWORD_VALUE,
// Putting CSP_HASH after the delimitor, because CSP_HASH is not a valid // Putting CSP_HASH after the delimitor, because CSP_HASH is not a valid
// keyword (hash uses e.g. sha256, sha512) but we use CSP_HASH internally // keyword (hash uses e.g. sha256, sha512) but we use CSP_HASH internally
// to identify allowed hashes in ::allows. // to identify allowed hashes in ::allows.
CSP_HASH CSP_HASH
}; };
static const char* CSPStrKeywords[] = { // The keywords, in UTF-8 form.
"'self'", // CSP_SELF = 0 static const char* gCSPUTF8Keywords[] = {
"'unsafe-inline'", // CSP_UNSAFE_INLINE #define KEYWORD_UTF8_LITERAL(id_, string_) string_,
"'unsafe-eval'", // CSP_UNSAFE_EVAL FOR_EACH_CSP_KEYWORD(KEYWORD_UTF8_LITERAL)
"'none'", // CSP_NONE #undef KEYWORD_UTF8_LITERAL
"'nonce-", // CSP_NONCE
"require-sri-for", // CSP_REQUIRE_SRI_FOR
"'strict-dynamic'" // CSP_STRICT_DYNAMIC
// Remember: CSP_HASH is not supposed to be used
}; };
inline const char* CSP_EnumToKeyword(enum CSPKeyword aKey) // The keywords, in UTF-16 form.
{ static const char16_t* gCSPUTF16Keywords[] = {
// Make sure all elements in enum CSPKeyword got added to CSPStrKeywords. #define KEYWORD_UTF16_LITERAL(id_, string_) u"" string_,
static_assert((sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0]) == FOR_EACH_CSP_KEYWORD(KEYWORD_UTF16_LITERAL)
static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)), #undef KEYWORD_UTF16_LITERAL
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords"); };
if (static_cast<uint32_t>(aKey) < static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)) { #undef FOR_EACH_CSP_KEYWORD
return CSPStrKeywords[static_cast<uint32_t>(aKey)];
inline const char* CSP_EnumToUTF8Keyword(enum CSPKeyword aKey)
{
// Make sure all elements in enum CSPKeyword got added to gCSPUTF8Keywords.
static_assert((sizeof(gCSPUTF8Keywords) / sizeof(gCSPUTF8Keywords[0]) ==
CSP_LAST_KEYWORD_VALUE),
"CSP_LAST_KEYWORD_VALUE != length(gCSPUTF8Keywords)");
if (static_cast<uint32_t>(aKey) <
static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)) {
return gCSPUTF8Keywords[static_cast<uint32_t>(aKey)];
} }
return "error: invalid keyword in CSP_EnumToKeyword"; return "error: invalid keyword in CSP_EnumToUTF8Keyword";
} }
inline CSPKeyword CSP_KeywordToEnum(const nsAString& aKey) inline const char16_t* CSP_EnumToUTF16Keyword(enum CSPKeyword aKey)
{
// Make sure all elements in enum CSPKeyword got added to gCSPUTF16Keywords.
static_assert((sizeof(gCSPUTF16Keywords) / sizeof(gCSPUTF16Keywords[0]) ==
CSP_LAST_KEYWORD_VALUE),
"CSP_LAST_KEYWORD_VALUE != length(gCSPUTF16Keywords)");
if (static_cast<uint32_t>(aKey) <
static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)) {
return gCSPUTF16Keywords[static_cast<uint32_t>(aKey)];
}
return u"error: invalid keyword in CSP_EnumToUTF16Keyword";
}
inline CSPKeyword CSP_UTF16KeywordToEnum(const nsAString& aKey)
{ {
nsString lowerKey = PromiseFlatString(aKey); nsString lowerKey = PromiseFlatString(aKey);
ToLowerCase(lowerKey); ToLowerCase(lowerKey);
static_assert(CSP_LAST_KEYWORD_VALUE ==
(sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0])),
"CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) { for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
if (lowerKey.EqualsASCII(CSPStrKeywords[i])) { if (lowerKey.Equals(gCSPUTF16Keywords[i])) {
return static_cast<CSPKeyword>(i); return static_cast<CSPKeyword>(i);
} }
} }

Просмотреть файл

@ -263,9 +263,8 @@ class CSPValidator final : public nsCSPSrcVisitor {
return true; return true;
default: default:
NS_ConvertASCIItoUTF16 keyword(CSP_EnumToKeyword(src.getKeyword())); FormatError("csp.error.illegal-keyword",
nsDependentString(CSP_EnumToUTF16Keyword(src.getKeyword())));
FormatError("csp.error.illegal-keyword", keyword);
return false; return false;
} }
}; };