Bug 1890277: part 4) Add CSPParser support for the trusted-types directive, guarded behind the Trusted Types pref. r=tschuster,webidl,smaug

Differential Revision: https://phabricator.services.mozilla.com/D207274
This commit is contained in:
Mirko Brodesser
2024-04-25 13:59:56 +00:00
parent 6ec9fc0d2a
commit 59c22b11ca
7 changed files with 146 additions and 3 deletions

View File

@@ -66,6 +66,7 @@ interface nsIContentSecurityPolicy : nsISerializable
STYLE_SRC_ELEM_DIRECTIVE = 23,
STYLE_SRC_ATTR_DIRECTIVE = 24,
REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE = 25,
TRUSTED_TYPES_DIRECTIVE = 26,
};
/**

View File

@@ -207,7 +207,9 @@ invalidNumberOfTrustedTypesForDirectiveValues = Received an invalid number of to
# LOCALIZATION NOTE (invalidRequireTrustedTypesForDirectiveValue):
# %1$S is the passed token
invalidRequireTrustedTypesForDirectiveValue = Received an invalid token for the require-trusted-types-for directive: %1$S; expected script
# LOCALIZATION NOTE (invalidTrustedTypesExpression):
# %1$S is the passed token
invalidTrustedTypesExpression = Received an invalid token for the 'trusted-types' directive: %1$S
# LOCALIZATION NOTE (CSPMessagePrefix):
# Do not translate "Content-Security-Policy", only handle spacing for the colon.

View File

@@ -865,6 +865,94 @@ void nsCSPParser::handleRequireTrustedTypesForDirective(nsCSPDirective* aDir) {
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 nsCSPTrustedTypesDirectiveExpression(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"));
@@ -883,8 +971,9 @@ nsCSPDirective* nsCSPParser::directiveName() {
CSPDirective directive = CSP_StringToCSPDirective(mCurToken);
if (directive == nsIContentSecurityPolicy::NO_DIRECTIVE ||
(!StaticPrefs::dom_security_trusted_types_enabled() &&
directive ==
nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE)) {
(directive ==
nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE ||
directive == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE))) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag,
"couldNotProcessUnknownDirective", params);
@@ -1071,6 +1160,11 @@ void nsCSPParser::directive() {
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;

View File

@@ -73,6 +73,7 @@ class nsCSPParser {
void reportURIList(nsCSPDirective* aDir);
void sandboxFlagList(nsCSPDirective* aDir);
void handleRequireTrustedTypesForDirective(nsCSPDirective* aDir);
void handleTrustedTypesDirective(nsCSPDirective* aDir);
void sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs);
nsCSPBaseSrc* sourceExpression();
nsCSPSchemeSrc* schemeSource();

View File

@@ -23,6 +23,7 @@
#include "nsServiceManagerUtils.h"
#include "nsWhitespaceTokenizer.h"
#include "mozilla/Assertions.h"
#include "mozilla/Components.h"
#include "mozilla/dom/CSPDictionariesBinding.h"
#include "mozilla/dom/Document.h"
@@ -1029,6 +1030,23 @@ void nsCSPRequireTrustedTypesForDirectiveValue::toString(
aOutStr.Append(mValue);
}
/* =============== nsCSPTrustedTypesDirectiveExpression =============== */
nsCSPTrustedTypesDirectiveExpression::nsCSPTrustedTypesDirectiveExpression(
const nsAString& aExpression)
: mExpression{aExpression} {}
bool nsCSPTrustedTypesDirectiveExpression::visit(
nsCSPSrcVisitor* aVisitor) const {
MOZ_ASSERT_UNREACHABLE(
"Should only be called for other overloads of this method.");
return false;
}
void nsCSPTrustedTypesDirectiveExpression::toString(nsAString& aOutStr) const {
aOutStr.Append(mExpression);
}
/* ===== nsCSPDirective ====================== */
nsCSPDirective::nsCSPDirective(CSPDirective aDirective) {
@@ -1311,6 +1329,9 @@ bool nsCSPDirective::allowsAllInlineBehavior(CSPDirective aDir) const {
void nsCSPDirective::toString(nsAString& outStr) const {
// Append directive name
outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
MOZ_ASSERT(!mSrcs.IsEmpty());
outStr.AppendLiteral(" ");
// Append srcs
@@ -1454,6 +1475,13 @@ void nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const {
outCSP.mRequire_trusted_types_for.Value() = std::move(srcs);
return;
case nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE:
outCSP.mTrusted_types.Construct();
// Here, "srcs" represents tt-expressions
// (https://w3c.github.io/trusted-types/dist/spec/#trusted-types-csp-directive).
outCSP.mTrusted_types.Value() = std::move(srcs);
return;
default:
NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
}

View File

@@ -94,6 +94,7 @@ static const char* CSPStrDirectives[] = {
"style-src-elem", // STYLE_SRC_ELEM_DIRECTIVE
"style-src-attr", // STYLE_SRC_ATTR_DIRECTIVE
"require-trusted-types-for", // REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE
"trusted-types", // TRUSTED_TYPES_DIRECTIVE
};
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) {
@@ -400,6 +401,20 @@ class nsCSPRequireTrustedTypesForDirectiveValue : public nsCSPBaseSrc {
const nsString mValue;
};
/* =============== nsCSPTrustedTypesDirectiveExpression =============== */
class nsCSPTrustedTypesDirectiveExpression : public nsCSPBaseSrc {
public:
explicit nsCSPTrustedTypesDirectiveExpression(const nsAString& aExpression);
virtual ~nsCSPTrustedTypesDirectiveExpression() = default;
bool visit(nsCSPSrcVisitor* aVisitor) const override;
void toString(nsAString& aOutStr) const override;
private:
const nsString mExpression;
};
/* =============== nsCSPSrcVisitor ================== */
class nsCSPSrcVisitor {
@@ -435,6 +450,7 @@ class nsCSPDirective {
virtual void toString(nsAString& outStr) const;
void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;
// Takes ownership of the passed sources.
virtual void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs) {
mSrcs = aSrcs.Clone();
}

View File

@@ -33,6 +33,7 @@ dictionary CSP {
sequence<DOMString> script-src-elem;
sequence<DOMString> script-src-attr;
sequence<DOMString> require-trusted-types-for;
sequence<DOMString> trusted-types;
};
[GenerateToJSON]