Commit d0d26288 authored by Lucas Gadani's avatar Lucas Gadani Committed by Commit Bot

CSP: Parse and report violations from Content-Security-Policy-Report-Only

headers in the browser process.

Bug: 759184
Change-Id: Iefb33fa8ec2264f3e224d5a3ab92417160848fdf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1958692Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Lucas Gadani <lfg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#726618}
parent 7e4c17e7
......@@ -177,7 +177,8 @@ ContentSecurityPolicy::ContentSecurityPolicy(
// struct.
ContentSecurityPolicy::ContentSecurityPolicy(
network::mojom::ContentSecurityPolicyPtr csp)
: report_endpoints(std::move(csp->report_endpoints)),
: header("", csp->type, csp->source),
report_endpoints(std::move(csp->report_endpoints)),
use_reporting_api(csp->use_reporting_api) {
for (auto& directive : csp->directives)
directives.emplace_back(std::move(directive));
......
......@@ -34,6 +34,7 @@ int LLVMFuzzerInitialize(int* argc, char*** argv) {
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
ContentSecurityPolicy policy;
policy.Parse(GURL("https://example.com/"),
network::mojom::ContentSecurityPolicyType::kEnforce,
std::string(reinterpret_cast<const char*>(data), size));
return 0;
......
......@@ -359,13 +359,23 @@ bool ContentSecurityPolicy::Parse(const GURL& base_url,
std::string header_value;
while (headers.EnumerateHeader(&iter, "content-security-policy",
&header_value)) {
if (!Parse(base_url, header_value))
if (!Parse(base_url, network::mojom::ContentSecurityPolicyType::kEnforce,
header_value))
return false;
}
iter = 0;
while (headers.EnumerateHeader(&iter, "content-security-policy-report-only",
&header_value)) {
if (!Parse(base_url, network::mojom::ContentSecurityPolicyType::kReport,
header_value))
return false;
}
return true;
}
bool ContentSecurityPolicy::Parse(const GURL& base_url,
bool ContentSecurityPolicy::Parse(
const GURL& base_url,
network::mojom::ContentSecurityPolicyType type,
base::StringPiece header_value) {
// RFC7230, section 3.2.2 specifies that headers appearing multiple times can
// be combined with a comma. Walk the header string, and parse each comma
......@@ -375,6 +385,7 @@ bool ContentSecurityPolicy::Parse(const GURL& base_url,
base::SPLIT_WANT_NONEMPTY)) {
DirectivesMap directives = ParseHeaderValue(header);
auto content_security_policy_ptr = mojom::ContentSecurityPolicy::New();
content_security_policy_ptr->type = type;
auto frame_ancestors = directives.find("frame-ancestors");
if (frame_ancestors != directives.end()) {
......
......@@ -33,7 +33,9 @@ class COMPONENT_EXPORT(NETWORK_CPP) ContentSecurityPolicy {
bool Parse(const GURL& request_url, const net::HttpResponseHeaders& headers);
// Parses a Content-Security-Policy |header|.
bool Parse(const GURL& base_url, base::StringPiece header);
bool Parse(const GURL& base_url,
network::mojom::ContentSecurityPolicyType type,
base::StringPiece header);
const std::vector<mojom::ContentSecurityPolicyPtr>&
content_security_policies() {
......
......@@ -73,6 +73,12 @@ struct CSPDirective {
struct ContentSecurityPolicy {
array<CSPDirective> directives;
// When set to kReport, this CSP will not enforce the directives, just report
// the violations.
ContentSecurityPolicyType type = kEnforce;
ContentSecurityPolicySource source = kHTTP;
// Whether this CSP policy uses the new reporting API.
// https://w3c.github.io/reporting/
bool use_reporting_api = false;
......
......@@ -62,8 +62,8 @@ blink::ContentSecurityPolicyPtr ConvertToBlink(ContentSecurityPolicyPtr csp) {
for (auto& endpoint : csp->report_endpoints)
report_endpoints.push_back(String::FromUTF8(endpoint));
return blink::ContentSecurityPolicy::New(std::move(directives),
csp->use_reporting_api,
return blink::ContentSecurityPolicy::New(std::move(directives), csp->type,
csp->source, csp->use_reporting_api,
std::move(report_endpoints));
}
......@@ -336,12 +336,26 @@ mojom::blink::FetchAPIResponsePtr FetchResponseData::PopulateFetchAPIResponse(
content_security_policy_header)) {
network::ContentSecurityPolicy policy;
if (policy.Parse(request_url,
network::mojom::ContentSecurityPolicyType::kEnforce,
StringUTF8Adaptor(content_security_policy_header)
.AsStringPiece())) {
response->content_security_policy =
ConvertToBlink(policy.TakeContentSecurityPolicy());
}
}
if (HeaderList()->Get("content-security-policy-report-only",
content_security_policy_header)) {
network::ContentSecurityPolicy policy;
if (policy.Parse(request_url,
network::mojom::ContentSecurityPolicyType::kReport,
StringUTF8Adaptor(content_security_policy_header)
.AsStringPiece())) {
auto blink_policies =
ConvertToBlink(policy.TakeContentSecurityPolicy());
for (auto& policy : blink_policies)
response->content_security_policy.push_back(std::move(policy));
}
}
}
return response;
}
......
......@@ -4,6 +4,8 @@
#include "third_party/blink/renderer/core/fetch/fetch_response_data.h"
#include "base/test/scoped_feature_list.h"
#include "services/network/public/cpp/features.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_response.mojom-blink.h"
#include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
......@@ -263,4 +265,23 @@ TEST_F(FetchResponseDataTest, DefaultResponseTime) {
EXPECT_FALSE(internal_response->ResponseTime().is_null());
}
TEST_F(FetchResponseDataTest, ContentSecurityPolicy) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
network::features::kOutOfBlinkFrameAncestors);
FetchResponseData* internal_response = CreateInternalResponse();
internal_response->HeaderList()->Append("content-security-policy",
"frame-ancestors 'none'");
internal_response->HeaderList()->Append("content-security-policy-report-only",
"frame-ancestors 'none'");
mojom::blink::FetchAPIResponsePtr fetch_api_response =
internal_response->PopulateFetchAPIResponse(KURL());
auto& csp = fetch_api_response->content_security_policy;
EXPECT_EQ(csp.size(), 2U);
EXPECT_EQ(csp[0]->type, network::mojom::ContentSecurityPolicyType::kEnforce);
EXPECT_EQ(csp[1]->type, network::mojom::ContentSecurityPolicyType::kReport);
}
} // namespace blink
<!DOCTYPE html>
<html>
<meta name="timeout" content="long">
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<title>Blocked frames are reported correctly</title>
</head>
<body>
<iframe src="support/content-security-policy-report-only.sub.html?policy=report-uri%20../../support/report.py%3Fop=put%26reportID={{$id:uuid()}}%3B%20frame-ancestors%20'none'"></iframe>
<script async defer src="../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-ancestors%20'none'&reportID={{$id}}"></script>
</body>
</html>
<!DOCTYPE html>
<html>
<body>
<p>This is an IFrame sending a Content-Security-Policy-Report-Only header containing "{{GET[policy]}}".</p>
</body>
</html>
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