Commit 3c01b472 authored by Antonio Sartori's avatar Antonio Sartori Committed by Commit Bot

Parse plugin-types CSP directive in services/network

This CL adds logic for parsing the plugin-types
Content-Security-Policy directive to the services/network CSP parser.

Bug: 1094909
Change-Id: I05dd7caf7b2d3c513b80f2265723bc1a0be04b3b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2346657
Commit-Queue: Antonio Sartori <antoniosartori@chromium.org>
Reviewed-by: default avatarCamille Lamy <clamy@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804774}
parent 60cfc65c
...@@ -95,6 +95,7 @@ std::string URLDataSource::GetContentSecurityPolicy( ...@@ -95,6 +95,7 @@ std::string URLDataSource::GetContentSecurityPolicy(
case network::mojom::CSPDirectiveName::ImgSrc: case network::mojom::CSPDirectiveName::ImgSrc:
case network::mojom::CSPDirectiveName::ManifestSrc: case network::mojom::CSPDirectiveName::ManifestSrc:
case network::mojom::CSPDirectiveName::MediaSrc: case network::mojom::CSPDirectiveName::MediaSrc:
case network::mojom::CSPDirectiveName::PluginTypes:
case network::mojom::CSPDirectiveName::PrefetchSrc: case network::mojom::CSPDirectiveName::PrefetchSrc:
case network::mojom::CSPDirectiveName::ReportURI: case network::mojom::CSPDirectiveName::ReportURI:
case network::mojom::CSPDirectiveName::Sandbox: case network::mojom::CSPDirectiveName::Sandbox:
......
...@@ -92,6 +92,7 @@ static CSPDirectiveName CSPFallback(CSPDirectiveName directive, ...@@ -92,6 +92,7 @@ static CSPDirectiveName CSPFallback(CSPDirectiveName directive,
case CSPDirectiveName::FormAction: case CSPDirectiveName::FormAction:
case CSPDirectiveName::FrameAncestors: case CSPDirectiveName::FrameAncestors:
case CSPDirectiveName::NavigateTo: case CSPDirectiveName::NavigateTo:
case CSPDirectiveName::PluginTypes:
case CSPDirectiveName::ReportTo: case CSPDirectiveName::ReportTo:
case CSPDirectiveName::ReportURI: case CSPDirectiveName::ReportURI:
case CSPDirectiveName::RequireTrustedTypesFor: case CSPDirectiveName::RequireTrustedTypesFor:
...@@ -140,6 +141,7 @@ const char* ErrorMessage(CSPDirectiveName directive) { ...@@ -140,6 +141,7 @@ const char* ErrorMessage(CSPDirectiveName directive) {
case CSPDirectiveName::ManifestSrc: case CSPDirectiveName::ManifestSrc:
case CSPDirectiveName::MediaSrc: case CSPDirectiveName::MediaSrc:
case CSPDirectiveName::ObjectSrc: case CSPDirectiveName::ObjectSrc:
case CSPDirectiveName::PluginTypes:
case CSPDirectiveName::PrefetchSrc: case CSPDirectiveName::PrefetchSrc:
case CSPDirectiveName::ReportTo: case CSPDirectiveName::ReportTo:
case CSPDirectiveName::ReportURI: case CSPDirectiveName::ReportURI:
...@@ -637,6 +639,41 @@ mojom::CSPSourceListPtr ParseSourceList( ...@@ -637,6 +639,41 @@ mojom::CSPSourceListPtr ParseSourceList(
return directive; return directive;
} }
// Checks whether |expression| is a plugin type matching the regex:
// [^\s/]+\/[^\s/]+
// We assume |expression| does not contain any whitespaces.
bool IsPluginType(base::StringPiece expression) {
auto* it = expression.begin();
auto* end = expression.end();
int count_1 = EatChar(&it, end, [](char c) { return c != '/'; });
if (it == end || *it != '/')
return false;
++it;
int count_2 = EatChar(&it, end, [](char c) { return c != '/'; });
return count_1 >= 1 && count_2 >= 1 && it == end;
}
std::vector<std::string> ParsePluginTypes(
base::StringPiece value,
std::vector<std::string>& parsing_errors) {
std::vector<std::string> out;
for (const auto expression : base::SplitStringPiece(
value, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
if (IsPluginType(expression))
out.emplace_back(expression.as_string());
else {
parsing_errors.emplace_back(base::StringPrintf(
"Invalid plugin type in 'plugin-types' Content Security Policy "
"directive: '%s'.",
expression.as_string().c_str()));
}
}
return out;
}
// Parses a reporting directive. // Parses a reporting directive.
// https://w3c.github.io/webappsec-csp/#directives-reporting // https://w3c.github.io/webappsec-csp/#directives-reporting
// TODO(lfg): The report-to should be treated as a single token according to the // TODO(lfg): The report-to should be treated as a single token according to the
...@@ -771,6 +808,13 @@ void AddContentSecurityPolicyFromHeader(base::StringPiece header, ...@@ -771,6 +808,13 @@ void AddContentSecurityPolicyFromHeader(base::StringPiece header,
directive.second.as_string().c_str())); directive.second.as_string().c_str()));
} }
break; break;
case CSPDirectiveName::PluginTypes:
// If the plugin-types directive is present, then always initialize
// `out->plugin_types` to be non-null, since only the plugin types
// explicitly listed will be allowed..
out->plugin_types =
ParsePluginTypes(directive.second, out->parsing_errors);
break;
// We check the following three directives so that we do not trigger a // We check the following three directives so that we do not trigger a
// warning because of an unrecognized directive. However, we skip // warning because of an unrecognized directive. However, we skip
...@@ -1070,6 +1114,8 @@ CSPDirectiveName ToCSPDirectiveName(const std::string& name) { ...@@ -1070,6 +1114,8 @@ CSPDirectiveName ToCSPDirectiveName(const std::string& name) {
return CSPDirectiveName::MediaSrc; return CSPDirectiveName::MediaSrc;
if (name == "object-src") if (name == "object-src")
return CSPDirectiveName::ObjectSrc; return CSPDirectiveName::ObjectSrc;
if (name == "plugin-types")
return CSPDirectiveName::PluginTypes;
if (name == "prefetch-src") if (name == "prefetch-src")
return CSPDirectiveName::PrefetchSrc; return CSPDirectiveName::PrefetchSrc;
if (name == "report-uri") if (name == "report-uri")
...@@ -1134,6 +1180,8 @@ std::string ToString(CSPDirectiveName name) { ...@@ -1134,6 +1180,8 @@ std::string ToString(CSPDirectiveName name) {
return "media-src"; return "media-src";
case CSPDirectiveName::ObjectSrc: case CSPDirectiveName::ObjectSrc:
return "object-src"; return "object-src";
case CSPDirectiveName::PluginTypes:
return "plugin-types";
case CSPDirectiveName::PrefetchSrc: case CSPDirectiveName::PrefetchSrc:
return "prefetch-src"; return "prefetch-src";
case CSPDirectiveName::ReportURI: case CSPDirectiveName::ReportURI:
......
...@@ -39,6 +39,16 @@ struct TestData { ...@@ -39,6 +39,16 @@ struct TestData {
ExpectedResult expected_result = ExpectedResult(); ExpectedResult expected_result = ExpectedResult();
}; };
std::vector<mojom::ContentSecurityPolicyPtr> ParseCSP(std::string expression) {
scoped_refptr<net::HttpResponseHeaders> headers(
new net::HttpResponseHeaders("HTTP/1.1 200 OK"));
headers->SetHeader("Content-Security-Policy", expression);
std::vector<mojom::ContentSecurityPolicyPtr> policies;
AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"),
&policies);
return policies;
}
static void TestFrameAncestorsCSPParser(const std::string& header, static void TestFrameAncestorsCSPParser(const std::string& header,
const ExpectedResult* expected_result) { const ExpectedResult* expected_result) {
scoped_refptr<net::HttpResponseHeaders> headers( scoped_refptr<net::HttpResponseHeaders> headers(
...@@ -569,6 +579,51 @@ TEST(ContentSecurityPolicy, ParseDirectives) { ...@@ -569,6 +579,51 @@ TEST(ContentSecurityPolicy, ParseDirectives) {
} }
} }
TEST(ContentSecurityPolicy, ParsePluginTypes) {
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("plugin-types application/pdf text/plain invalid a/a/a");
EXPECT_EQ(policies[0]->directives.size(), 0u);
EXPECT_TRUE(policies[0]->plugin_types.has_value());
EXPECT_EQ(policies[0]->plugin_types.value().size(), 2u);
EXPECT_EQ(policies[0]->plugin_types.value()[0], "application/pdf");
EXPECT_EQ(policies[0]->plugin_types.value()[1], "text/plain");
EXPECT_EQ(policies[0]->parsing_errors.size(), 2u);
EXPECT_EQ(policies[0]->parsing_errors[0],
"Invalid plugin type in 'plugin-types' Content Security Policy "
"directive: 'invalid'.");
EXPECT_EQ(policies[0]->parsing_errors[1],
"Invalid plugin type in 'plugin-types' Content Security Policy "
"directive: 'a/a/a'.");
}
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("plugin-types ; default-src 'self'");
EXPECT_TRUE(policies[0]->plugin_types.has_value());
EXPECT_EQ(policies[0]->plugin_types.value().size(), 0u);
EXPECT_EQ(policies[0]->parsing_errors.size(), 0u);
}
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("plugin-types 'self' ; default-src 'self'");
EXPECT_TRUE(policies[0]->plugin_types.has_value());
EXPECT_EQ(policies[0]->plugin_types.value().size(), 0u);
EXPECT_EQ(policies[0]->parsing_errors.size(), 1u);
EXPECT_EQ(policies[0]->parsing_errors[0],
"Invalid plugin type in 'plugin-types' Content Security Policy "
"directive: ''self''.");
}
{
std::vector<mojom::ContentSecurityPolicyPtr> policies =
ParseCSP("default-src 'self'");
EXPECT_FALSE(policies[0]->plugin_types.has_value());
EXPECT_EQ(policies[0]->parsing_errors.size(), 0u);
}
}
TEST(ContentSecurityPolicy, ParseReportEndpoint) { TEST(ContentSecurityPolicy, ParseReportEndpoint) {
// report-uri directive. // report-uri directive.
{ {
......
...@@ -109,6 +109,7 @@ enum CSPDirectiveName { ...@@ -109,6 +109,7 @@ enum CSPDirectiveName {
MediaSrc, MediaSrc,
NavigateTo, NavigateTo,
ObjectSrc, ObjectSrc,
PluginTypes,
PrefetchSrc, PrefetchSrc,
ReportTo, ReportTo,
ReportURI, ReportURI,
...@@ -153,6 +154,12 @@ struct ContentSecurityPolicy { ...@@ -153,6 +154,12 @@ struct ContentSecurityPolicy {
// Set of reporting endpoints to which violation reports are sent. // Set of reporting endpoints to which violation reports are sent.
array<string> report_endpoints; array<string> report_endpoints;
// Plugin types specified by the CSP. If `null`, no plugin-types directive is
// specified and hence all types are allowed. If an empty array, then the
// plugin-types directive is specified with no types, hence no types are
// allowed.
array<string>? plugin_types;
// An array containing a set of errors occurred while parsing the CSP header. // An array containing a set of errors occurred while parsing the CSP header.
array<string> parsing_errors; array<string> parsing_errors;
}; };
......
...@@ -129,6 +129,10 @@ blink::ContentSecurityPolicyPtr ConvertToBlink( ...@@ -129,6 +129,10 @@ blink::ContentSecurityPolicyPtr ConvertToBlink(
policy_in->sandbox, ConvertToBlink(std::move(policy_in->header)), policy_in->sandbox, ConvertToBlink(std::move(policy_in->header)),
policy_in->use_reporting_api, policy_in->use_reporting_api,
ConvertToBlink(std::move(policy_in->report_endpoints)), ConvertToBlink(std::move(policy_in->report_endpoints)),
policy_in->plugin_types.has_value()
? base::Optional<WTF::Vector<WTF::String>>(
ConvertToBlink(std::move(policy_in->plugin_types.value())))
: base::nullopt,
ConvertToBlink(std::move(policy_in->parsing_errors))); 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