diff options
Diffstat (limited to 'dom/security/nsCSPParser.cpp')
-rw-r--r-- | dom/security/nsCSPParser.cpp | 159 |
1 files changed, 158 insertions, 1 deletions
diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp index 07812470a3..44c2131640 100644 --- a/dom/security/nsCSPParser.cpp +++ b/dom/security/nsCSPParser.cpp @@ -8,6 +8,7 @@ #include "mozilla/TextUtils.h" #include "mozilla/dom/Document.h" #include "mozilla/Preferences.h" +#include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_security.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" @@ -19,6 +20,9 @@ #include "nsServiceManagerUtils.h" #include "nsUnicharUtils.h" +#include <cstdint> +#include <utility> + using namespace mozilla; using namespace mozilla::dom; @@ -813,6 +817,142 @@ void nsCSPParser::sandboxFlagList(nsCSPDirective* aDir) { mPolicy->addDirective(aDir); } +// https://w3c.github.io/trusted-types/dist/spec/#integration-with-content-security-policy +static constexpr nsLiteralString kValidRequireTrustedTypesForDirectiveValue = + u"'script'"_ns; + +static bool IsValidRequireTrustedTypesForDirectiveValue( + const nsAString& aToken) { + return aToken.Equals(kValidRequireTrustedTypesForDirectiveValue); +} + +void nsCSPParser::handleRequireTrustedTypesForDirective(nsCSPDirective* aDir) { + // "srcs" start at index 1. Here "srcs" should represent Trusted Types' sink + // groups + // (https://w3c.github.io/trusted-types/dist/spec/#require-trusted-types-for-csp-directive). + + if (mCurDir.Length() != 2) { + nsString numberOfTokensStr; + + // Casting is required to avoid ambiguous function calls on some platforms. + numberOfTokensStr.AppendInt(static_cast<uint64_t>(mCurDir.Length())); + + AutoTArray<nsString, 1> numberOfTokensArr = {std::move(numberOfTokensStr)}; + logWarningErrorToConsole(nsIScriptError::errorFlag, + "invalidNumberOfTrustedTypesForDirectiveValues", + numberOfTokensArr); + return; + } + + mCurToken = mCurDir.LastElement(); + + CSPPARSERLOG( + ("nsCSPParser::handleRequireTrustedTypesForDirective, mCurToken: %s", + NS_ConvertUTF16toUTF8(mCurToken).get())); + + if (!IsValidRequireTrustedTypesForDirectiveValue(mCurToken)) { + AutoTArray<nsString, 1> token = {mCurToken}; + logWarningErrorToConsole(nsIScriptError::errorFlag, + "invalidRequireTrustedTypesForDirectiveValue", + token); + return; + } + + nsTArray<nsCSPBaseSrc*> srcs = { + new nsCSPRequireTrustedTypesForDirectiveValue(mCurToken)}; + + aDir->addSrcs(srcs); + mPolicy->addDirective(aDir); +} + +static constexpr auto kTrustedTypesKeywordAllowDuplicates = + u"'allow-duplicates'"_ns; +static constexpr auto kTrustedTypesKeywordNone = u"'none'"_ns; + +static bool IsValidTrustedTypesKeyword(const nsAString& aToken) { + // tt-keyword = "'allow-duplicates'" / "'none'" + return aToken.Equals(kTrustedTypesKeywordAllowDuplicates) || + aToken.Equals(kTrustedTypesKeywordNone); +} + +static bool IsValidTrustedTypesWildcard(const nsAString& aToken) { + // tt-wildcard = "*" + return aToken.Length() == 1 && aToken.First() == WILDCARD; +} + +static bool IsValidTrustedTypesPolicyNameChar(char16_t aChar) { + // tt-policy-name = 1*( ALPHA / DIGIT / "-" / "#" / "=" / "_" / "/" / "@" / + // "." / "%") + return nsContentUtils::IsAlphanumeric(aChar) || aChar == DASH || + aChar == NUMBER_SIGN || aChar == EQUALS || aChar == UNDERLINE || + aChar == SLASH || aChar == ATSYMBOL || aChar == DOT || + aChar == PERCENT_SIGN; +} + +static bool IsValidTrustedTypesPolicyName(const nsAString& aToken) { + // tt-policy-name = 1*( ALPHA / DIGIT / "-" / "#" / "=" / "_" / "/" / "@" / + // "." / "%") + + if (aToken.IsEmpty()) { + return false; + } + + for (uint32_t i = 0; i < aToken.Length(); ++i) { + if (!IsValidTrustedTypesPolicyNameChar(aToken.CharAt(i))) { + return false; + } + } + + return true; +} + +// https://w3c.github.io/trusted-types/dist/spec/#trusted-types-csp-directive +static bool IsValidTrustedTypesExpression(const nsAString& aToken) { + // tt-expression = tt-policy-name / tt-keyword / tt-wildcard + return IsValidTrustedTypesPolicyName(aToken) || + IsValidTrustedTypesKeyword(aToken) || + IsValidTrustedTypesWildcard(aToken); +} + +void nsCSPParser::handleTrustedTypesDirective(nsCSPDirective* aDir) { + CSPPARSERLOG(("nsCSPParser::handleTrustedTypesDirective")); + + nsTArray<nsCSPBaseSrc*> trustedTypesExpressions; + + // "srcs" start and index 1. Here they should represent the tt-expressions + // (https://w3c.github.io/trusted-types/dist/spec/#trusted-types-csp-directive). + for (uint32_t i = 1; i < mCurDir.Length(); ++i) { + mCurToken = mCurDir[i]; + + CSPPARSERLOG(("nsCSPParser::handleTrustedTypesDirective, mCurToken: %s", + NS_ConvertUTF16toUTF8(mCurToken).get())); + + if (!IsValidTrustedTypesExpression(mCurToken)) { + AutoTArray<nsString, 1> token = {mCurToken}; + logWarningErrorToConsole(nsIScriptError::errorFlag, + "invalidTrustedTypesExpression", token); + + for (auto* trustedTypeExpression : trustedTypesExpressions) { + delete trustedTypeExpression; + } + + return; + } + + trustedTypesExpressions.AppendElement( + new nsCSPTrustedTypesDirectivePolicyName(mCurToken)); + } + + if (trustedTypesExpressions.IsEmpty()) { + // No tt-expression is equivalent to 'none', see + // <https://w3c.github.io/trusted-types/dist/spec/#trusted-types-csp-directive>. + trustedTypesExpressions.AppendElement(new nsCSPKeywordSrc(CSP_NONE)); + } + + aDir->addSrcs(trustedTypesExpressions); + mPolicy->addDirective(aDir); +} + // directive-value = *( WSP / <VCHAR except ";" and ","> ) void nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs) { CSPPARSERLOG(("nsCSPParser::directiveValue")); @@ -829,7 +969,11 @@ nsCSPDirective* nsCSPParser::directiveName() { // Check if it is a valid directive CSPDirective directive = CSP_StringToCSPDirective(mCurToken); - if (directive == nsIContentSecurityPolicy::NO_DIRECTIVE) { + if (directive == nsIContentSecurityPolicy::NO_DIRECTIVE || + (!StaticPrefs::dom_security_trusted_types_enabled() && + (directive == + nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE || + directive == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE))) { AutoTArray<nsString, 1> params = {mCurToken}; logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotProcessUnknownDirective", params); @@ -1008,6 +1152,19 @@ void nsCSPParser::directive() { return; } + // Special case handling since these directives don't contain source lists. + if (CSP_IsDirective( + mCurDir[0], + nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE)) { + handleRequireTrustedTypesForDirective(cspDir); + return; + } + + if (cspDir->equals(nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE)) { + handleTrustedTypesDirective(cspDir); + 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; |