Commit e5acbc53 authored by Yutaka Hirano's avatar Yutaka Hirano Committed by Commit Bot

Introduce COEP reporting for CORP (1/3)

1: [this]
2: https://crrev.com/c/2075002
3: https://crrev.com/c/2076223

This series of CLs implements https://github.com/mikewest/corpp/pull/9.
We introduce network::mojom::CrossOriginEmbedderPolicyReporter and
its implementation content::CrossOriginEmbedderPolicyReporter, implement
the reporting logic in content::CrossOriginEmbedderPolicyReporter and
the CORP check, and plumb the mojo interface.

This CL introduces network::mojom::CrossOriginEmbedderPolicyReporter and
its implementation content::CrossOriginEmbedderPolicyReporter.

Bug: 1052764
Change-Id: I7ccce3e39c760393bf2d1b73786cf2a7ae838fde
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2074177Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747170}
parent db7a17d7
...@@ -2631,6 +2631,8 @@ jumbo_source_set("browser") { ...@@ -2631,6 +2631,8 @@ jumbo_source_set("browser") {
if (enable_reporting) { if (enable_reporting) {
sources += [ sources += [
"net/cross_origin_embedder_policy_reporter.cc",
"net/cross_origin_embedder_policy_reporter.h",
"net/reporting_service_proxy.cc", "net/reporting_service_proxy.cc",
"net/reporting_service_proxy.h", "net/reporting_service_proxy.h",
] ]
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/net/cross_origin_embedder_policy_reporter.h"
#include "base/values.h"
#include "content/public/browser/storage_partition.h"
#include "services/network/public/mojom/network_context.mojom.h"
namespace content {
CrossOriginEmbedderPolicyReporter::CrossOriginEmbedderPolicyReporter(
StoragePartition* storage_partition,
const GURL& context_url,
const base::Optional<std::string>& endpoint,
const base::Optional<std::string>& report_only_endpoint)
: storage_partition_(storage_partition),
context_url_(context_url),
endpoint_(endpoint),
report_only_endpoint_(report_only_endpoint) {
DCHECK(storage_partition_);
}
CrossOriginEmbedderPolicyReporter::~CrossOriginEmbedderPolicyReporter() =
default;
void CrossOriginEmbedderPolicyReporter::QueueCorpViolationReport(
const GURL& blocked_url,
bool report_only) {
const base::Optional<std::string>& endpoint =
report_only ? report_only_endpoint_ : endpoint_;
if (!endpoint) {
return;
}
url::Replacements<char> replacements;
replacements.ClearUsername();
replacements.ClearPassword();
base::DictionaryValue body;
body.SetString("type", "corp");
body.SetString("blocked-url",
blocked_url.ReplaceComponents(replacements).spec());
storage_partition_->GetNetworkContext()->QueueReport(
"coep", *endpoint, context_url_, /*user_agent=*/base::nullopt,
std::move(body));
}
void CrossOriginEmbedderPolicyReporter::Clone(
mojo::PendingReceiver<network::mojom::CrossOriginEmbedderPolicyReporter>
receiver) {
receiver_set_.Add(this, std::move(receiver));
}
} // namespace content
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_NET_CROSS_ORIGIN_EMBEDDER_POLICY_REPORTER_H_
#define CONTENT_BROWSER_NET_CROSS_ORIGIN_EMBEDDER_POLICY_REPORTER_H_
#include <string>
#include "base/optional.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h"
#include "url/gurl.h"
namespace content {
class StoragePartition;
// Used to report (potential) COEP violations.
// A CrossOriginEmbedderPolicyReporter is retained by an object that represents
// a "setting object" in the browser process such as RenderFrameHostImpl and
// DedicatedWorkerHost. They create a mojo endpoint using Clone and pass it
// around. For example, it's sent to the Network Service via
// network.mojom.URLLoaderFactoryParam.coep_reporter.
// Any functions other than the destructor must not be called after the
// associated StoragePartition is destructed.
// TODO(yhirano): This currently only sends reports to the network. Notify
// the event to the associated ReportingObserver.
class CONTENT_EXPORT CrossOriginEmbedderPolicyReporter final
: public network::mojom::CrossOriginEmbedderPolicyReporter {
public:
CrossOriginEmbedderPolicyReporter(
StoragePartition* storage_partition,
const GURL& context_url,
const base::Optional<std::string>& endpoint,
const base::Optional<std::string>& report_only_endpoint);
~CrossOriginEmbedderPolicyReporter() override;
CrossOriginEmbedderPolicyReporter(const CrossOriginEmbedderPolicyReporter&) =
delete;
CrossOriginEmbedderPolicyReporter& operator=(
const CrossOriginEmbedderPolicyReporter&) = delete;
// network::mojom::CrossOriginEmbedderPolicyReporter implementation.
void QueueCorpViolationReport(const GURL& blocked_url,
bool report_only) override;
void Clone(
mojo::PendingReceiver<network::mojom::CrossOriginEmbedderPolicyReporter>
receiver) override;
private:
// See the class comment.
StoragePartition* const storage_partition_;
const GURL context_url_;
const base::Optional<std::string> endpoint_;
const base::Optional<std::string> report_only_endpoint_;
mojo::ReceiverSet<network::mojom::CrossOriginEmbedderPolicyReporter>
receiver_set_;
};
} // namespace content
#endif // CONTENT_BROWSER_NET_CROSS_ORIGIN_EMBEDDER_POLICY_REPORTER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/net/cross_origin_embedder_policy_reporter.h"
#include <vector>
#include "base/test/task_environment.h"
#include "base/values.h"
#include "content/public/test/test_storage_partition.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/test/test_network_context.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
class TestNetworkContext : public network::TestNetworkContext {
public:
struct Report {
Report(const std::string& type,
const std::string& group,
const GURL& url,
base::Value body)
: type(type), group(group), url(url), body(std::move(body)) {}
std::string type;
std::string group;
GURL url;
base::Value body;
};
void QueueReport(const std::string& type,
const std::string& group,
const GURL& url,
const base::Optional<std::string>& user_agent,
base::Value body) override {
DCHECK(!user_agent);
reports_.push_back(Report(type, group, url, std::move(body)));
}
const std::vector<Report>& reports() const { return reports_; }
private:
std::vector<Report> reports_;
};
class CrossOriginEmbedderPolicyReporterTest : public testing::Test {
public:
using Report = TestNetworkContext::Report;
CrossOriginEmbedderPolicyReporterTest() {
storage_partition_.set_network_context(&network_context_);
}
StoragePartition* storage_partition() { return &storage_partition_; }
const TestNetworkContext& network_context() const { return network_context_; }
base::Value CreateBody(base::StringPiece s) {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetKey("type", base::Value("corp"));
dict.SetKey("blocked-url", base::Value(s));
return dict;
}
private:
base::test::TaskEnvironment task_environment_;
TestNetworkContext network_context_;
TestStoragePartition storage_partition_;
};
TEST_F(CrossOriginEmbedderPolicyReporterTest, NullEndpoints) {
const GURL kContextUrl("https://example.com/path");
CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
base::nullopt, base::nullopt);
reporter.QueueCorpViolationReport(GURL("https://www1.example.com/y"),
/*report_only=*/false);
reporter.QueueCorpViolationReport(GURL("https://www2.example.com/x"),
/*report_only=*/true);
EXPECT_TRUE(network_context().reports().empty());
}
TEST_F(CrossOriginEmbedderPolicyReporterTest, Basic) {
const GURL kContextUrl("https://example.com/path");
CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
"e1", "e2");
reporter.QueueCorpViolationReport(
GURL("https://www1.example.com/x#foo?bar=baz"),
/*report_only=*/false);
reporter.QueueCorpViolationReport(GURL("http://www2.example.com:41/y"),
/*report_only=*/true);
ASSERT_EQ(2u, network_context().reports().size());
const Report& r1 = network_context().reports()[0];
const Report& r2 = network_context().reports()[1];
EXPECT_EQ(r1.type, "coep");
EXPECT_EQ(r1.group, "e1");
EXPECT_EQ(r1.url, kContextUrl);
EXPECT_EQ(r1.body, CreateBody("https://www1.example.com/x#foo?bar=baz"));
EXPECT_EQ(r2.type, "coep");
EXPECT_EQ(r2.group, "e2");
EXPECT_EQ(r2.url, kContextUrl);
EXPECT_EQ(r2.body, CreateBody("http://www2.example.com:41/y"));
}
TEST_F(CrossOriginEmbedderPolicyReporterTest, UserAndPass) {
const GURL kContextUrl("https://example.com/path");
CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
"e1", "e2");
reporter.QueueCorpViolationReport(GURL("https://u:p@www1.example.com/x"),
/*report_only=*/false);
reporter.QueueCorpViolationReport(GURL("https://u:p@www2.example.com/y"),
/*report_only=*/true);
ASSERT_EQ(2u, network_context().reports().size());
const Report& r1 = network_context().reports()[0];
const Report& r2 = network_context().reports()[1];
EXPECT_EQ(r1.type, "coep");
EXPECT_EQ(r1.group, "e1");
EXPECT_EQ(r1.url, kContextUrl);
EXPECT_EQ(r1.body, CreateBody("https://www1.example.com/x"));
EXPECT_EQ(r2.type, "coep");
EXPECT_EQ(r2.group, "e2");
EXPECT_EQ(r2.url, kContextUrl);
EXPECT_EQ(r2.body, CreateBody("https://www2.example.com/y"));
}
TEST_F(CrossOriginEmbedderPolicyReporterTest, Clone) {
const GURL kContextUrl("https://example.com/path");
CrossOriginEmbedderPolicyReporter reporter(storage_partition(), kContextUrl,
"e1", "e2");
mojo::Remote<network::mojom::CrossOriginEmbedderPolicyReporter> remote;
reporter.Clone(remote.BindNewPipeAndPassReceiver());
remote->QueueCorpViolationReport(GURL("https://www1.example.com/x"),
/*report_only=*/false);
remote->QueueCorpViolationReport(GURL("https://www2.example.com/y"),
/*report_only=*/true);
remote.FlushForTesting();
ASSERT_EQ(2u, network_context().reports().size());
const Report& r1 = network_context().reports()[0];
const Report& r2 = network_context().reports()[1];
EXPECT_EQ(r1.type, "coep");
EXPECT_EQ(r1.group, "e1");
EXPECT_EQ(r1.url, kContextUrl);
EXPECT_EQ(r1.body, CreateBody("https://www1.example.com/x"));
EXPECT_EQ(r2.type, "coep");
EXPECT_EQ(r2.group, "e2");
EXPECT_EQ(r2.url, kContextUrl);
EXPECT_EQ(r2.body, CreateBody("https://www2.example.com/y"));
}
} // namespace
} // namespace content
...@@ -1697,6 +1697,7 @@ test("content_unittests") { ...@@ -1697,6 +1697,7 @@ test("content_unittests") {
"../browser/native_file_system/native_file_system_file_writer_impl_unittest.cc", "../browser/native_file_system/native_file_system_file_writer_impl_unittest.cc",
"../browser/native_file_system/native_file_system_handle_base_unittest.cc", "../browser/native_file_system/native_file_system_handle_base_unittest.cc",
"../browser/native_file_system/native_file_system_manager_impl_unittest.cc", "../browser/native_file_system/native_file_system_manager_impl_unittest.cc",
"../browser/net/cross_origin_embedder_policy_reporter_unittest.cc",
"../browser/net/network_quality_observer_impl_unittest.cc", "../browser/net/network_quality_observer_impl_unittest.cc",
"../browser/network_context_client_base_impl_unittest.cc", "../browser/network_context_client_base_impl_unittest.cc",
"../browser/notification_service_impl_unittest.cc", "../browser/notification_service_impl_unittest.cc",
......
...@@ -4,12 +4,24 @@ ...@@ -4,12 +4,24 @@
module network.mojom; module network.mojom;
import "url/mojom/url.mojom";
// https://mikewest.github.io/corpp/#integration-html // https://mikewest.github.io/corpp/#integration-html
enum CrossOriginEmbedderPolicyValue { enum CrossOriginEmbedderPolicyValue {
kNone, kNone,
kRequireCorp, kRequireCorp,
}; };
// Reports potential COEP violations. This is mainly used by the CORP check
// in the network service. Implemented in the browser process.
interface CrossOriginEmbedderPolicyReporter {
// Queues a report of a CORP violation caused by COEP.
QueueCorpViolationReport(url.mojom.Url blocked_url, bool report_only);
// Connects a new pipe to this instance.
Clone(pending_receiver<CrossOriginEmbedderPolicyReporter> receiver);
};
// Corresponding to the "embedder policy" concept in the spec. // Corresponding to the "embedder policy" concept in the spec.
// TODO(yhirano): Attach a spec link. // TODO(yhirano): Attach a spec link.
struct CrossOriginEmbedderPolicy { struct CrossOriginEmbedderPolicy {
......
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