Commit d9e3492c authored by Antonio Sartori's avatar Antonio Sartori Committed by Commit Bot

Add 'trusted-types' directive to network CSP code

The Content Security Policies code under services/network and the
relative mojo types still misses support for a few CSP directives. We
need to support all CSP directives in order to use it as a replacement
to the Blink types, and also if we want to add Content Security
Policies to the Policy Container (see attached bugs).

This CL implements support for the directive 'trusted-types'.

Bug: 1021462,1149272
Change-Id: I78797427f9239e1b57aca3e71d4de83e9897e7a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2539907Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Antonio Sartori <antoniosartori@chromium.org>
Cr-Commit-Position: refs/heads/master@{#829138}
parent cde0f142
......@@ -51,6 +51,16 @@ network::mojom::ContentSecurityPolicyPtr BuildContentSecurityPolicy(
policy->require_trusted_types_for = policy_in.require_trusted_types_for;
if (policy_in.trusted_types) {
std::vector<std::string> list;
for (const auto& type : policy_in.trusted_types->list) {
list.emplace_back(type.Utf8());
}
policy->trusted_types = network::mojom::CSPTrustedTypes::New(
std::move(list), policy_in.trusted_types->allow_any,
policy_in.trusted_types->allow_duplicates);
}
return policy;
}
......
......@@ -743,6 +743,53 @@ network::mojom::CSPRequireTrustedTypesFor ParseRequireTrustedTypesFor(
return network::mojom::CSPRequireTrustedTypesFor::None;
}
// This implements tt-policy-name from
// https://w3c.github.io/webappsec-trusted-types/dist/spec/#trusted-types-csp-directive/
bool IsValidTrustedTypesPolicyName(base::StringPiece value) {
return base::ranges::all_of(value, [](char c) {
return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) ||
base::Contains("-#=_/@.%", c);
});
}
// Parse the 'trusted-types' directive.
// https://w3c.github.io/webappsec-trusted-types/dist/spec/#trusted-types-csp-directive
network::mojom::CSPTrustedTypesPtr ParseTrustedTypes(
base::StringPiece value,
std::vector<std::string>& parsing_errors) {
auto out = network::mojom::CSPTrustedTypes::New();
std::vector<base::StringPiece> pieces =
base::SplitStringPiece(value, base::kWhitespaceASCII,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (pieces.size() == 1 && pieces[0] == "'none'")
return out;
for (const auto expression : pieces) {
if (expression == "*")
out->allow_any = true;
else if (expression == "'allow-duplicates'")
out->allow_duplicates = true;
else if (expression == "'none'") {
parsing_errors.emplace_back(
"The value of the Content Security Policy directive "
"'trusted_types' contains an invalid policy: 'none'. "
"It will be ignored. "
"Note that 'none' has no effect unless it is the only "
"expression in the directive value.");
} else if (IsValidTrustedTypesPolicyName(expression))
out->list.emplace_back(expression);
else {
parsing_errors.emplace_back(base::StringPrintf(
"The value of the Content Security Policy directive "
"'trusted_types' contains an invalid policy: '%s'. "
"It will be ignored.",
expression.as_string().c_str()));
}
}
return out;
}
// Parses a reporting directive.
// https://w3c.github.io/webappsec-csp/#directives-reporting
// TODO(lfg): The report-to should be treated as a single token according to the
......@@ -899,12 +946,16 @@ void AddContentSecurityPolicyFromHeader(base::StringPiece header,
ParseRequireTrustedTypesFor(directive.second);
break;
// We check the following three directives so that we do not trigger a
// warning because of an unrecognized directive. However, we skip
// parsing them for now since we do not need these directives here (they
// are parsed and enforced in the blink CSP parser).
case CSPDirectiveName::BlockAllMixedContent:
case CSPDirectiveName::TrustedTypes:
out->trusted_types =
ParseTrustedTypes(directive.second, out->parsing_errors);
break;
// We check the following directive so that we do not trigger a warning
// because of an unrecognized directive. However, we skip parsing it for
// now since we do not need it here (it is parsed and enforced in the
// blink CSP parser).
case CSPDirectiveName::BlockAllMixedContent:
break;
case CSPDirectiveName::ReportTo:
......
......@@ -671,6 +671,50 @@ TEST(ContentSecurityPolicy, ParseRequireTrustedTypesFor) {
}
}
TEST(ContentSecurityPolicy, ParseTrustedTypes) {
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("script-src 'none'");
EXPECT_EQ(policies[0]->directives.size(), 1u);
EXPECT_FALSE(policies[0]->trusted_types);
}
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("trusted-types 'none'");
EXPECT_EQ(policies[0]->directives.size(), 0u);
EXPECT_TRUE(policies[0]->trusted_types);
EXPECT_EQ(policies[0]->trusted_types->list.size(), 0u);
EXPECT_FALSE(policies[0]->trusted_types->allow_any);
EXPECT_FALSE(policies[0]->trusted_types->allow_duplicates);
EXPECT_EQ(policies[0]->trusted_types->list.size(), 0u);
EXPECT_EQ(policies[0]->parsing_errors.size(), 0u);
}
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("trusted-types policy 'none' other_policy@ invalid~policy");
EXPECT_EQ(policies[0]->directives.size(), 0u);
EXPECT_TRUE(policies[0]->trusted_types);
EXPECT_EQ(policies[0]->trusted_types->list.size(), 2u);
EXPECT_EQ(policies[0]->trusted_types->list[0], "policy");
EXPECT_EQ(policies[0]->trusted_types->list[1], "other_policy@");
EXPECT_FALSE(policies[0]->trusted_types->allow_any);
EXPECT_FALSE(policies[0]->trusted_types->allow_duplicates);
EXPECT_EQ(policies[0]->parsing_errors.size(), 2u);
EXPECT_EQ(
policies[0]->parsing_errors[0],
"The value of the Content Security Policy directive 'trusted_types' "
"contains an invalid policy: 'none'. It will be ignored. "
"Note that 'none' has no effect unless it is the only "
"expression in the directive value.");
EXPECT_EQ(
policies[0]->parsing_errors[1],
"The value of the Content Security Policy directive 'trusted_types' "
"contains an invalid policy: 'invalid~policy'. It will be ignored.");
}
}
TEST(ContentSecurityPolicy, ParseReportEndpoint) {
// report-uri directive.
{
......
......@@ -132,6 +132,21 @@ enum CSPRequireTrustedTypesFor {
Script = 1,
};
// The parsed value of the CSP directive 'trusted-types'.
// https://w3c.github.io/webappsec-trusted-types/dist/spec/#trusted-types-csp-directive
struct CSPTrustedTypes {
// The list of policies allowed by the 'trusted-types' directive.
array<string> list;
// This is true if the directive value contains the wildcard * (meaning all
// policy names are allowed).
bool allow_any = false;
// This is true if the directive value contains the keyword 'allow-duplicates'
// (which allows creating policies with a name that was already used).
bool allow_duplicates = false;
};
struct ContentSecurityPolicy {
map<CSPDirectiveName, CSPSourceList> directives;
......@@ -170,6 +185,14 @@ struct ContentSecurityPolicy {
CSPRequireTrustedTypesFor require_trusted_types_for =
CSPRequireTrustedTypesFor.None;
// The parsed value of the directive 'trusted-types'.
// https://w3c.github.io/webappsec-trusted-types/dist/spec/#trusted-types-csp-directive
// Note: If this is null, the directive was not present. On the other side, if
// this is a default CSPTrustedTypes struct with empty list, it means that the
// directive was present with empty value, so policies may not be created and
// no DOM XSS injection sinks can be used at all.
CSPTrustedTypes? trusted_types;
// An array containing a set of errors occurred while parsing the CSP header.
array<string> parsing_errors;
};
......
......@@ -71,6 +71,14 @@ struct WebContentSecurityPolicyDirective {
WebContentSecurityPolicySourceList source_list;
};
// TODO(arthursonzogni): Remove this when BeginNavigation will be sent directly
// from blink.
struct WebCSPTrustedTypes {
WebVector<WebString> list;
bool allow_any;
bool allow_duplicates;
};
// TODO(arthursonzogni): Remove this when BeginNavigation will be sent directly
// from blink.
struct WebContentSecurityPolicy {
......@@ -82,6 +90,7 @@ struct WebContentSecurityPolicy {
WebString header;
bool use_reporting_api;
network::mojom::CSPRequireTrustedTypesFor require_trusted_types_for;
base::Optional<WebCSPTrustedTypes> trusted_types;
};
} // namespace blink
......
......@@ -254,6 +254,17 @@ WebString ConvertToPublic(
};
}
// TODO(arthursonzogni): Remove this when BeginNavigation will be sent directly
// from blink.
base::Optional<WebCSPTrustedTypes> ConvertToPublic(
network::mojom::blink::CSPTrustedTypesPtr trusted_types) {
if (!trusted_types)
return base::nullopt;
return WebCSPTrustedTypes{std::move(trusted_types->list),
trusted_types->allow_any,
trusted_types->allow_duplicates};
}
// TODO(arthursonzogni): Remove this when BeginNavigation will be sent directly
// from blink.
WebContentSecurityPolicy ConvertToPublic(
......@@ -273,7 +284,8 @@ WebContentSecurityPolicy ConvertToPublic(
std::move(policy->report_endpoints),
policy->header->header_value,
policy->use_reporting_api,
policy->require_trusted_types_for};
policy->require_trusted_types_for,
ConvertToPublic(std::move(policy->trusted_types))};
}
} // namespace
......
......@@ -121,6 +121,14 @@ WTF::Vector<WTF::String> ConvertToBlink(std::vector<std::string> in) {
return out;
}
blink::CSPTrustedTypesPtr ConvertToBlink(CSPTrustedTypesPtr trusted_types) {
if (!trusted_types)
return nullptr;
return blink::CSPTrustedTypes::New(ConvertToBlink(trusted_types->list),
trusted_types->allow_any,
trusted_types->allow_duplicates);
}
blink::ContentSecurityPolicyPtr ConvertToBlink(
ContentSecurityPolicyPtr policy_in) {
return blink::ContentSecurityPolicy::New(
......@@ -134,6 +142,7 @@ blink::ContentSecurityPolicyPtr ConvertToBlink(
ConvertToBlink(std::move(policy_in->plugin_types.value())))
: base::nullopt,
policy_in->require_trusted_types_for,
ConvertToBlink(std::move(policy_in->trusted_types)),
ConvertToBlink(std::move(policy_in->parsing_errors)));
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment