Bug 1890277: part 2) Add require-trusted-types-for directive to CSP parser, guarded behind the Trusted Types pref. r=tschuster,webidl,smaug

Differential Revision: https://phabricator.services.mozilla.com/D206998
This commit is contained in:
Mirko Brodesser 2024-04-17 12:24:18 +00:00
parent 2fcd7e5c5c
commit db3ca28481
7 changed files with 155 additions and 38 deletions

View File

@ -40,31 +40,32 @@ interface nsIContentSecurityPolicy : nsISerializable
* add it to the CSPStrDirectives array in nsCSPUtils.h.
*/
cenum CSPDirective : 8 {
NO_DIRECTIVE = 0,
DEFAULT_SRC_DIRECTIVE = 1,
SCRIPT_SRC_DIRECTIVE = 2,
OBJECT_SRC_DIRECTIVE = 3,
STYLE_SRC_DIRECTIVE = 4,
IMG_SRC_DIRECTIVE = 5,
MEDIA_SRC_DIRECTIVE = 6,
FRAME_SRC_DIRECTIVE = 7,
FONT_SRC_DIRECTIVE = 8,
CONNECT_SRC_DIRECTIVE = 9,
REPORT_URI_DIRECTIVE = 10,
FRAME_ANCESTORS_DIRECTIVE = 11,
REFLECTED_XSS_DIRECTIVE = 12,
BASE_URI_DIRECTIVE = 13,
FORM_ACTION_DIRECTIVE = 14,
WEB_MANIFEST_SRC_DIRECTIVE = 15,
UPGRADE_IF_INSECURE_DIRECTIVE = 16,
CHILD_SRC_DIRECTIVE = 17,
BLOCK_ALL_MIXED_CONTENT = 18,
SANDBOX_DIRECTIVE = 19,
WORKER_SRC_DIRECTIVE = 20,
SCRIPT_SRC_ELEM_DIRECTIVE = 21,
SCRIPT_SRC_ATTR_DIRECTIVE = 22,
STYLE_SRC_ELEM_DIRECTIVE = 23,
STYLE_SRC_ATTR_DIRECTIVE = 24,
NO_DIRECTIVE = 0,
DEFAULT_SRC_DIRECTIVE = 1,
SCRIPT_SRC_DIRECTIVE = 2,
OBJECT_SRC_DIRECTIVE = 3,
STYLE_SRC_DIRECTIVE = 4,
IMG_SRC_DIRECTIVE = 5,
MEDIA_SRC_DIRECTIVE = 6,
FRAME_SRC_DIRECTIVE = 7,
FONT_SRC_DIRECTIVE = 8,
CONNECT_SRC_DIRECTIVE = 9,
REPORT_URI_DIRECTIVE = 10,
FRAME_ANCESTORS_DIRECTIVE = 11,
REFLECTED_XSS_DIRECTIVE = 12,
BASE_URI_DIRECTIVE = 13,
FORM_ACTION_DIRECTIVE = 14,
WEB_MANIFEST_SRC_DIRECTIVE = 15,
UPGRADE_IF_INSECURE_DIRECTIVE = 16,
CHILD_SRC_DIRECTIVE = 17,
BLOCK_ALL_MIXED_CONTENT = 18,
SANDBOX_DIRECTIVE = 19,
WORKER_SRC_DIRECTIVE = 20,
SCRIPT_SRC_ELEM_DIRECTIVE = 21,
SCRIPT_SRC_ATTR_DIRECTIVE = 22,
STYLE_SRC_ELEM_DIRECTIVE = 23,
STYLE_SRC_ATTR_DIRECTIVE = 24,
REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE = 25,
};
/**

View File

@ -201,6 +201,13 @@ duplicateDirective = Duplicate %1$S directives detected. All but the first inst
# LOCALIZATION NOTE (couldntParseInvalidSandboxFlag):
# %1$S is the option that could not be understood
couldntParseInvalidSandboxFlag = Couldnt parse invalid sandbox flag %1$S
# LOCALIZATION NOTE (invalidNumberOfTrustedTypesForDirectiveValues):
# %1$S is the number of passed tokens.
invalidNumberOfTrustedTypesForDirectiveValues = Received an invalid number of tokens for the 'require-trusted-types-for' directive: %1$S; expected 1
# 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 (CSPMessagePrefix):
# Do not translate "Content-Security-Policy", only handle spacing for the colon.

View File

@ -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,54 @@ 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);
}
// directive-value = *( WSP / <VCHAR except ";" and ","> )
void nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs) {
CSPPARSERLOG(("nsCSPParser::directiveValue"));
@ -829,7 +881,10 @@ 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)) {
AutoTArray<nsString, 1> params = {mCurToken};
logWarningErrorToConsole(nsIScriptError::warningFlag,
"couldNotProcessUnknownDirective", params);
@ -1008,6 +1063,14 @@ 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;
}
// 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

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

View File

@ -243,6 +243,20 @@ void CSP_LogMessage(const nsAString& aMessage, const nsAString& aSourceName,
console->LogMessage(error);
}
CSPDirective CSP_StringToCSPDirective(const nsAString& aDir) {
nsString lowerDir = PromiseFlatString(aDir);
ToLowerCase(lowerDir);
uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
for (uint32_t i = 1; i < numDirs; i++) {
if (lowerDir.EqualsASCII(CSPStrDirectives[i])) {
return static_cast<CSPDirective>(i);
}
}
return nsIContentSecurityPolicy::NO_DIRECTIVE;
}
/**
* Combines CSP_LogMessage and CSP_GetLocalizedStr into one call.
*/
@ -997,6 +1011,24 @@ void nsCSPSandboxFlags::toString(nsAString& outStr) const {
outStr.Append(mFlags);
}
/* ===== nsCSPRequireTrustedTypesForDirectiveValue ===================== */
nsCSPRequireTrustedTypesForDirectiveValue::
nsCSPRequireTrustedTypesForDirectiveValue(const nsAString& aValue)
: mValue{aValue} {}
bool nsCSPRequireTrustedTypesForDirectiveValue::visit(
nsCSPSrcVisitor* aVisitor) const {
MOZ_ASSERT_UNREACHABLE(
"This method should only be called for other overloads of this method.");
return false;
}
void nsCSPRequireTrustedTypesForDirectiveValue::toString(
nsAString& aOutStr) const {
aOutStr.Append(mValue);
}
/* ===== nsCSPDirective ====================== */
nsCSPDirective::nsCSPDirective(CSPDirective aDirective) {
@ -1414,6 +1446,14 @@ void nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const {
outCSP.mScript_src_attr.Value() = std::move(srcs);
return;
case nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE:
outCSP.mRequire_trusted_types_for.Construct();
// Here, the srcs represent the sink group
// (https://w3c.github.io/trusted-types/dist/spec/#integration-with-content-security-policy).
outCSP.mRequire_trusted_types_for.Value() = std::move(srcs);
return;
default:
NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
}

View File

@ -93,24 +93,14 @@ static const char* CSPStrDirectives[] = {
"script-src-attr", // SCRIPT_SRC_ATTR_DIRECTIVE
"style-src-elem", // STYLE_SRC_ELEM_DIRECTIVE
"style-src-attr", // STYLE_SRC_ATTR_DIRECTIVE
"require-trusted-types-for", // REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE
};
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) {
return CSPStrDirectives[static_cast<uint32_t>(aDir)];
}
inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir) {
nsString lowerDir = PromiseFlatString(aDir);
ToLowerCase(lowerDir);
uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
for (uint32_t i = 1; i < numDirs; i++) {
if (lowerDir.EqualsASCII(CSPStrDirectives[i])) {
return static_cast<CSPDirective>(i);
}
}
return nsIContentSecurityPolicy::NO_DIRECTIVE;
}
CSPDirective CSP_StringToCSPDirective(const nsAString& aDir);
#define FOR_EACH_CSP_KEYWORD(MACRO) \
MACRO(CSP_SELF, "'self'") \
@ -396,6 +386,20 @@ class nsCSPSandboxFlags : public nsCSPBaseSrc {
nsString mFlags;
};
/* =============== nsCSPRequireTrustedTypesForDirectiveValue =============== */
class nsCSPRequireTrustedTypesForDirectiveValue : public nsCSPBaseSrc {
public:
explicit nsCSPRequireTrustedTypesForDirectiveValue(const nsAString& aValue);
virtual ~nsCSPRequireTrustedTypesForDirectiveValue() = default;
bool visit(nsCSPSrcVisitor* aVisitor) const override;
void toString(nsAString& aOutStr) const override;
private:
const nsString mValue;
};
/* =============== nsCSPSrcVisitor ================== */
class nsCSPSrcVisitor {

View File

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