Add credentials to same-origin Report uploads
When the reporting endpoint is same-origin with the page generating
reports, the reporting API specifies that credentials should be included
with the report delivery.
This change enables that behaviour for V1 reporting endpoints only --
any endpoints configured with the Report-To header are unaffected. This
CL corrects the way that we distinguish V0 from V1 endpoints at delivery
time, which also corrects the metrics collection for delivered reports,
and adds a test for a case which was previously missed.
Bug: 1163645
Change-Id: I8fcd934b3026b57374e7e7df31c9af9a8d93962d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3169200
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Maksim Orlovich <[email protected]>
Reviewed-by: Ken Buchanan <[email protected]>
Reviewed-by: Mike West <[email protected]>
Reviewed-by: Christian Dullweber <[email protected]>
Commit-Queue: Ian Clelland <[email protected]>
Cr-Commit-Position: refs/heads/main@{#929239}
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index c1f4bd10..deac4e87 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -125,6 +125,7 @@
#include "content/public/test/mock_download_manager.h"
#include "content/public/test/test_utils.h"
#include "mojo/public/cpp/bindings/remote.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_access_result.h"
@@ -904,7 +905,7 @@
void SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const net::IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) override {
NOTREACHED();
}
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 5835e6e..6520b9b 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -3560,8 +3560,8 @@
// reporting cache.
if (!reporting_endpoints_.empty()) {
GetStoragePartition()->GetNetworkContext()->SetDocumentReportingEndpoints(
- GetReportingSource(), params.origin,
- isolation_info_.network_isolation_key(), reporting_endpoints_);
+ GetReportingSource(), params.origin, isolation_info_,
+ reporting_endpoints_);
}
// When the frame hosts a different document, its state must be replicated
diff --git a/net/reporting/reporting_cache.h b/net/reporting/reporting_cache.h
index 39b4421..0405e1b 100644
--- a/net/reporting/reporting_cache.h
+++ b/net/reporting/reporting_cache.h
@@ -27,6 +27,7 @@
namespace net {
class ReportingContext;
+class IsolationInfo;
// The cache holds undelivered reports and clients (per-origin endpoint
// configurations) in memory. (It is not responsible for persisting them.)
@@ -173,13 +174,11 @@
// received Reporting-Endpoints header.
// |reporting_source| is the token identifying the document or worker with
// which this header was received, and may not be empty.
- // |origin| is the origin of the document or worker represented by
- // |reporting_source|, and |network_isolation_key| is the appropriate NIK for
- // that source. These two parameters are currently needed because the new
- // Reporting API currently shares the cache, delivery agent and
- // ReportingEndpoint struct with the old API.
+ // |isolation_info| is the appropriate network isolation info struct for that
+ // source, and is used for determining credentials to send with reports.
virtual void OnParsedReportingEndpointsHeader(
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
std::vector<ReportingEndpoint> parsed_header) = 0;
// Gets all the origins of clients in the cache.
@@ -304,13 +303,23 @@
int priority,
int weight) = 0;
- // Sets a V1 named endpoint with the given key for |reporting_source|,
+ // Sets a V1 named endpoint with the given key for `reporting_source`,
// bypassing header parsing. This method inserts a single endpoint while
- // leaving the existing configuration for that source intact.
+ // leaving the existing configuration for that source intact. If any
+ // endpoints already exist for this source, then `isolation_info` must
+ // match the value that was previously associated with it.
virtual void SetV1EndpointForTesting(
const ReportingEndpointGroupKey& group_key,
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
const GURL& url) = 0;
+
+ // Gets the isolation info associated with `reporting_source`, used when
+ // determining which credentials to send for a given report. If
+ // `reporting_source` is nullopt, as when a report is being delivered to a V0
+ // reporting endpoint group, this always will return an empty site.
+ virtual IsolationInfo GetIsolationInfoForEndpoint(
+ const ReportingEndpoint& endpoint) const = 0;
};
// Persistent storage for Reporting reports and clients.
diff --git a/net/reporting/reporting_cache_impl.cc b/net/reporting/reporting_cache_impl.cc
index 909103268..32a80c8c 100644
--- a/net/reporting/reporting_cache_impl.cc
+++ b/net/reporting/reporting_cache_impl.cc
@@ -392,16 +392,21 @@
report->status != ReportingReport::Status::SUCCESS;
}));
document_endpoints_.erase(reporting_source);
+ isolation_info_.erase(reporting_source);
expired_sources_.erase(reporting_source);
context_->NotifyEndpointsUpdated();
}
void ReportingCacheImpl::OnParsedReportingEndpointsHeader(
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
std::vector<ReportingEndpoint> endpoints) {
DCHECK(!reporting_source.is_empty());
DCHECK(!endpoints.empty());
+ DCHECK_EQ(0u, document_endpoints_.count(reporting_source));
+ DCHECK_EQ(0u, isolation_info_.count(reporting_source));
document_endpoints_.insert({reporting_source, std::move(endpoints)});
+ isolation_info_.insert({reporting_source, isolation_info});
context_->NotifyEndpointsUpdated();
}
@@ -787,10 +792,14 @@
void ReportingCacheImpl::SetV1EndpointForTesting(
const ReportingEndpointGroupKey& group_key,
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
const GURL& url) {
DCHECK(!reporting_source.is_empty());
DCHECK(group_key.IsDocumentEndpoint());
DCHECK_EQ(reporting_source, group_key.reporting_source.value());
+ DCHECK_EQ(group_key.network_isolation_key,
+ isolation_info.network_isolation_key());
+
ReportingEndpoint::EndpointInfo info;
info.url = url;
ReportingEndpoint new_endpoint(group_key, info);
@@ -804,6 +813,15 @@
} else {
document_endpoints_.insert({reporting_source, {std::move(new_endpoint)}});
}
+ // If this is the first time we've used this reporting_source, then add the
+ // isolation info. Otherwise, ensure that it is the same as what was used
+ // previously.
+ if (isolation_info_.count(reporting_source) == 0) {
+ isolation_info_.insert({reporting_source, isolation_info});
+ } else {
+ DCHECK(isolation_info_.at(reporting_source)
+ .IsEqualForTesting(isolation_info)); // IN-TEST
+ }
context_->NotifyEndpointsUpdated();
}
@@ -865,6 +883,20 @@
context_->NotifyCachedClientsUpdated();
}
+IsolationInfo ReportingCacheImpl::GetIsolationInfoForEndpoint(
+ const ReportingEndpoint& endpoint) const {
+ // V0 endpoint groups do not support credentials.
+ if (!endpoint.group_key.reporting_source.has_value()) {
+ return IsolationInfo::CreatePartial(
+ IsolationInfo::RequestType::kOther,
+ endpoint.group_key.network_isolation_key);
+ }
+ const auto it =
+ isolation_info_.find(endpoint.group_key.reporting_source.value());
+ DCHECK(it != isolation_info_.end());
+ return it->second;
+}
+
ReportingCacheImpl::Client::Client(
const NetworkIsolationKey& network_isolation_key,
const url::Origin& origin)
diff --git a/net/reporting/reporting_cache_impl.h b/net/reporting/reporting_cache_impl.h
index ee886b9..20897fd 100644
--- a/net/reporting/reporting_cache_impl.h
+++ b/net/reporting/reporting_cache_impl.h
@@ -20,6 +20,7 @@
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "base/values.h"
+#include "net/base/isolation_info.h"
#include "net/reporting/reporting_cache.h"
#include "net/reporting/reporting_context.h"
#include "net/reporting/reporting_endpoint.h"
@@ -85,6 +86,7 @@
std::vector<ReportingEndpointGroup> parsed_header) override;
void OnParsedReportingEndpointsHeader(
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
std::vector<ReportingEndpoint> parsed_header) override;
std::set<url::Origin> GetAllOrigins() const override;
void RemoveClient(const NetworkIsolationKey& network_isolation_key,
@@ -126,7 +128,10 @@
int weight) override;
void SetV1EndpointForTesting(const ReportingEndpointGroupKey& group_key,
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
const GURL& url) override;
+ IsolationInfo GetIsolationInfoForEndpoint(
+ const ReportingEndpoint& endpoint) const override;
private:
// Represents the entire Report-To configuration for a (NIK, origin) pair.
@@ -366,7 +371,7 @@
std::multimap<GURL, EndpointMap::iterator> endpoint_its_by_url_;
// Reporting API V1 Cache:
- // The |document_endpoints_| member holds endpoint configuration for the V1
+ // The `document_endpoints_` member holds endpoint configuration for the V1
// API, configured through the Reporting-Endpoints HTTP header. These
// endpoints are strongly associated with the resource which configured them,
// and are only used for document reports.
@@ -376,9 +381,14 @@
std::map<base::UnguessableToken, std::vector<ReportingEndpoint>>
document_endpoints_;
+ // Isolation info for each reporting source. Used for determining credentials
+ // to send when delivering reports. This contains only V1 document endpoints.
+ std::map<base::UnguessableToken, IsolationInfo> isolation_info_;
+
// Reporting source tokens representing sources which have been destroyed.
- // The configuration in `document_endpoints_` for these sources can be
- // removed once all outstanding reports are delivered (or expired).
+ // The configuration in `document_endpoints_` and `isolation_info_` for these
+ // sources can be removed once all outstanding reports are delivered (or
+ // expired).
base::flat_set<base::UnguessableToken> expired_sources_;
SEQUENCE_CHECKER(sequence_checker_);
diff --git a/net/reporting/reporting_cache_unittest.cc b/net/reporting/reporting_cache_unittest.cc
index f51b84c..88d1984 100644
--- a/net/reporting/reporting_cache_unittest.cc
+++ b/net/reporting/reporting_cache_unittest.cc
@@ -189,6 +189,16 @@
const NetworkIsolationKey kNik_;
const NetworkIsolationKey kOtherNik_ =
NetworkIsolationKey(SchemefulSite(kOrigin1_), SchemefulSite(kOrigin2_));
+ const IsolationInfo kIsolationInfo1_ =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther,
+ kOrigin1_,
+ kOrigin1_,
+ SiteForCookies::FromOrigin(kOrigin1_));
+ const IsolationInfo kIsolationInfo2_ =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther,
+ kOrigin2_,
+ kOrigin2_,
+ SiteForCookies::FromOrigin(kOrigin2_));
const GURL kEndpoint1_ = GURL("https://endpoint1/");
const GURL kEndpoint2_ = GURL("https://endpoint2/");
const GURL kEndpoint3_ = GURL("https://endpoint3/");
@@ -957,15 +967,23 @@
base::UnguessableToken::Create();
LoadReportingClients();
+ NetworkIsolationKey network_isolation_key_1 =
+ kIsolationInfo1_.network_isolation_key();
+ NetworkIsolationKey network_isolation_key_2 =
+ kIsolationInfo2_.network_isolation_key();
+
cache()->SetV1EndpointForTesting(
- ReportingEndpointGroupKey(kNik_, *kReportingSource_, kOrigin1_, kGroup1_),
- *kReportingSource_, kUrl1_);
+ ReportingEndpointGroupKey(network_isolation_key_1, *kReportingSource_,
+ kOrigin1_, kGroup1_),
+ *kReportingSource_, kIsolationInfo1_, kUrl1_);
cache()->SetV1EndpointForTesting(
- ReportingEndpointGroupKey(kNik_, *kReportingSource_, kOrigin1_, kGroup2_),
- *kReportingSource_, kUrl2_);
+ ReportingEndpointGroupKey(network_isolation_key_1, *kReportingSource_,
+ kOrigin1_, kGroup2_),
+ *kReportingSource_, kIsolationInfo1_, kUrl2_);
cache()->SetV1EndpointForTesting(
- ReportingEndpointGroupKey(kNik_, reporting_source_2, kOrigin2_, kGroup1_),
- reporting_source_2, kUrl2_);
+ ReportingEndpointGroupKey(network_isolation_key_2, reporting_source_2,
+ kOrigin2_, kGroup1_),
+ reporting_source_2, kIsolationInfo2_, kUrl2_);
EXPECT_EQ(2u, cache()->GetReportingSourceCountForTesting());
EXPECT_TRUE(cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup1_));
@@ -1083,18 +1101,26 @@
const base::UnguessableToken reporting_source_2 =
base::UnguessableToken::Create();
+ NetworkIsolationKey network_isolation_key =
+ kIsolationInfo1_.network_isolation_key();
const ReportingEndpointGroupKey document_group_key_1 =
- ReportingEndpointGroupKey(kNik_, reporting_source_1, kOrigin1_, kGroup1_);
+ ReportingEndpointGroupKey(network_isolation_key, reporting_source_1,
+ kOrigin1_, kGroup1_);
const ReportingEndpointGroupKey document_group_key_2 =
- ReportingEndpointGroupKey(kNik_, reporting_source_1, kOrigin1_, kGroup2_);
+ ReportingEndpointGroupKey(network_isolation_key, reporting_source_1,
+ kOrigin1_, kGroup2_);
const ReportingEndpointGroupKey document_group_key_3 =
- ReportingEndpointGroupKey(kNik_, reporting_source_2, kOrigin1_, kGroup1_);
+ ReportingEndpointGroupKey(network_isolation_key, reporting_source_2,
+ kOrigin1_, kGroup1_);
- SetV1EndpointInCache(document_group_key_1, reporting_source_1, kEndpoint1_);
- SetV1EndpointInCache(document_group_key_2, reporting_source_1, kEndpoint2_);
- SetV1EndpointInCache(document_group_key_3, reporting_source_2, kEndpoint1_);
- const ReportingEndpointGroupKey kReportGroupKey =
- ReportingEndpointGroupKey(kNik_, reporting_source_1, kOrigin1_, kGroup1_);
+ SetV1EndpointInCache(document_group_key_1, reporting_source_1,
+ kIsolationInfo1_, kEndpoint1_);
+ SetV1EndpointInCache(document_group_key_2, reporting_source_1,
+ kIsolationInfo1_, kEndpoint2_);
+ SetV1EndpointInCache(document_group_key_3, reporting_source_2,
+ kIsolationInfo1_, kEndpoint1_);
+ const ReportingEndpointGroupKey kReportGroupKey = ReportingEndpointGroupKey(
+ network_isolation_key, reporting_source_1, kOrigin1_, kGroup1_);
std::vector<ReportingEndpoint> candidate_endpoints =
cache()->GetCandidateEndpointsForDelivery(kReportGroupKey);
ASSERT_EQ(1u, candidate_endpoints.size());
@@ -1107,12 +1133,17 @@
const base::UnguessableToken reporting_source =
base::UnguessableToken::Create();
- const ReportingEndpointGroupKey kDocumentGroupKey =
- ReportingEndpointGroupKey(kNik_, reporting_source, kOrigin1_, kGroup1_);
+ NetworkIsolationKey network_isolation_key =
+ kIsolationInfo1_.network_isolation_key();
- SetV1EndpointInCache(kDocumentGroupKey, reporting_source, kEndpoint1_);
+ const ReportingEndpointGroupKey kDocumentGroupKey = ReportingEndpointGroupKey(
+ network_isolation_key, reporting_source, kOrigin1_, kGroup1_);
+
+ SetV1EndpointInCache(kDocumentGroupKey, reporting_source, kIsolationInfo1_,
+ kEndpoint1_);
const ReportingEndpointGroupKey kNetworkReportGroupKey =
- ReportingEndpointGroupKey(kNik_, absl::nullopt, kOrigin1_, kGroup1_);
+ ReportingEndpointGroupKey(network_isolation_key, absl::nullopt, kOrigin1_,
+ kGroup1_);
std::vector<ReportingEndpoint> candidate_endpoints =
cache()->GetCandidateEndpointsForDelivery(kNetworkReportGroupKey);
ASSERT_EQ(0u, candidate_endpoints.size());
@@ -1124,12 +1155,17 @@
const base::UnguessableToken reporting_source =
base::UnguessableToken::Create();
- const ReportingEndpointGroupKey kDocumentGroupKey =
- ReportingEndpointGroupKey(kNik_, reporting_source, kOrigin1_, kGroup1_);
+ NetworkIsolationKey network_isolation_key =
+ kIsolationInfo1_.network_isolation_key();
- SetV1EndpointInCache(kDocumentGroupKey, reporting_source, kEndpoint1_);
+ const ReportingEndpointGroupKey kDocumentGroupKey = ReportingEndpointGroupKey(
+ network_isolation_key, reporting_source, kOrigin1_, kGroup1_);
+
+ SetV1EndpointInCache(kDocumentGroupKey, reporting_source, kIsolationInfo1_,
+ kEndpoint1_);
const ReportingEndpointGroupKey kOtherGroupKey = ReportingEndpointGroupKey(
- kNik_, base::UnguessableToken::Create(), kOrigin1_, kGroup1_);
+ network_isolation_key, base::UnguessableToken::Create(), kOrigin1_,
+ kGroup1_);
std::vector<ReportingEndpoint> candidate_endpoints =
cache()->GetCandidateEndpointsForDelivery(kOtherGroupKey);
ASSERT_EQ(0u, candidate_endpoints.size());
@@ -1142,41 +1178,60 @@
TEST_P(ReportingCacheTest, GetMixedCandidateEndpointsForDelivery) {
LoadReportingClients();
- // Set up V0 endpoint groups for this origin
- ASSERT_TRUE(SetEndpointInCache(kGroupKey11_, kEndpoint1_, kExpires1_));
- ASSERT_TRUE(SetEndpointInCache(kGroupKey11_, kEndpoint2_, kExpires1_));
- ASSERT_TRUE(SetEndpointInCache(kGroupKey12_, kEndpoint2_, kExpires1_));
- ASSERT_TRUE(SetEndpointInCache(kGroupKey21_, kEndpoint1_, kExpires1_));
+ // This test relies on proper NIKs being used, so set those up, and endpoint
+ // group keys to go with them.
+ NetworkIsolationKey network_isolation_key1 =
+ kIsolationInfo1_.network_isolation_key();
+ NetworkIsolationKey network_isolation_key2 =
+ kIsolationInfo2_.network_isolation_key();
+ ReportingEndpointGroupKey group_key_11 =
+ ReportingEndpointGroupKey(network_isolation_key1, kOrigin1_, kGroup1_);
+ ReportingEndpointGroupKey group_key_12 =
+ ReportingEndpointGroupKey(network_isolation_key1, kOrigin1_, kGroup2_);
+ ReportingEndpointGroupKey group_key_21 =
+ ReportingEndpointGroupKey(network_isolation_key2, kOrigin2_, kGroup1_);
- // Set up a V1 endpoint for a document at the same origin
+ // Set up V0 endpoint groups for this origin.
+ ASSERT_TRUE(SetEndpointInCache(group_key_11, kEndpoint1_, kExpires1_));
+ ASSERT_TRUE(SetEndpointInCache(group_key_11, kEndpoint2_, kExpires1_));
+ ASSERT_TRUE(SetEndpointInCache(group_key_12, kEndpoint2_, kExpires1_));
+ ASSERT_TRUE(SetEndpointInCache(group_key_21, kEndpoint1_, kExpires1_));
+
+ // Set up a V1 endpoint for a document at the same origin.
+ NetworkIsolationKey network_isolation_key =
+ kIsolationInfo1_.network_isolation_key();
const base::UnguessableToken reporting_source =
base::UnguessableToken::Create();
const ReportingEndpointGroupKey document_group_key =
- ReportingEndpointGroupKey(kNik_, reporting_source, kOrigin1_, kGroup1_);
- SetV1EndpointInCache(document_group_key, reporting_source, kEndpoint1_);
+ ReportingEndpointGroupKey(network_isolation_key1, reporting_source,
+ kOrigin1_, kGroup1_);
+ SetV1EndpointInCache(document_group_key, reporting_source, kIsolationInfo1_,
+ kEndpoint1_);
// This group key will match both the V1 endpoint, and two V0 endpoints. Only
// the V1 endpoint should be returned.
std::vector<ReportingEndpoint> candidate_endpoints =
cache()->GetCandidateEndpointsForDelivery(ReportingEndpointGroupKey(
- kNik_, reporting_source, kOrigin1_, kGroup1_));
+ network_isolation_key1, reporting_source, kOrigin1_, kGroup1_));
ASSERT_EQ(1u, candidate_endpoints.size());
EXPECT_EQ(document_group_key, candidate_endpoints[0].group_key);
// This group key has no reporting source, so only V0 endpoints can be
// returned.
- candidate_endpoints = cache()->GetCandidateEndpointsForDelivery(
- ReportingEndpointGroupKey(kNik_, absl::nullopt, kOrigin1_, kGroup1_));
+ candidate_endpoints =
+ cache()->GetCandidateEndpointsForDelivery(ReportingEndpointGroupKey(
+ network_isolation_key1, absl::nullopt, kOrigin1_, kGroup1_));
ASSERT_EQ(2u, candidate_endpoints.size());
- EXPECT_EQ(kGroupKey11_, candidate_endpoints[0].group_key);
- EXPECT_EQ(kGroupKey11_, candidate_endpoints[1].group_key);
+ EXPECT_EQ(group_key_11, candidate_endpoints[0].group_key);
+ EXPECT_EQ(group_key_11, candidate_endpoints[1].group_key);
// This group key has a reporting source, but no matching V1 endpoints have
// been configured, so we should fall back to the V0 endpoints.
- candidate_endpoints = cache()->GetCandidateEndpointsForDelivery(
- ReportingEndpointGroupKey(kNik_, reporting_source, kOrigin1_, kGroup2_));
+ candidate_endpoints =
+ cache()->GetCandidateEndpointsForDelivery(ReportingEndpointGroupKey(
+ network_isolation_key1, reporting_source, kOrigin1_, kGroup2_));
ASSERT_EQ(1u, candidate_endpoints.size());
- EXPECT_EQ(kGroupKey12_, candidate_endpoints[0].group_key);
+ EXPECT_EQ(group_key_12, candidate_endpoints[0].group_key);
}
TEST_P(ReportingCacheTest, GetCandidateEndpointsDifferentNik) {
@@ -1864,6 +1919,48 @@
EXPECT_TRUE(EndpointExistsInCache(group4, kEndpoint1_));
}
+TEST_P(ReportingCacheTest, GetIsolationInfoForEndpoint) {
+ LoadReportingClients();
+
+ NetworkIsolationKey network_isolation_key1 =
+ kIsolationInfo1_.network_isolation_key();
+
+ // Set up a V1 endpoint for this origin.
+ cache()->SetV1EndpointForTesting(
+ ReportingEndpointGroupKey(network_isolation_key1, *kReportingSource_,
+ kOrigin1_, kGroup1_),
+ *kReportingSource_, kIsolationInfo1_, kUrl1_);
+
+ // Set up a V0 endpoint group for this origin.
+ ReportingEndpointGroupKey group_key_11 =
+ ReportingEndpointGroupKey(network_isolation_key1, kOrigin1_, kGroup1_);
+ ASSERT_TRUE(SetEndpointInCache(group_key_11, kEndpoint1_, kExpires1_));
+
+ // For a V1 endpoint, ensure that the isolation info matches exactly what was
+ // passed in.
+ ReportingEndpoint endpoint =
+ cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup1_);
+ EXPECT_TRUE(endpoint);
+ IsolationInfo isolation_info_for_document =
+ cache()->GetIsolationInfoForEndpoint(endpoint);
+ EXPECT_TRUE(isolation_info_for_document.IsEqualForTesting(kIsolationInfo1_));
+ EXPECT_EQ(isolation_info_for_document.request_type(),
+ IsolationInfo::RequestType::kOther);
+
+ // For a V0 endpoint, ensure that site_for_cookies is null and that the NIK
+ // matches the cached endpoint.
+ ReportingEndpoint network_endpoint =
+ cache()->GetEndpointForTesting(group_key_11, kEndpoint1_);
+ EXPECT_TRUE(network_endpoint);
+ IsolationInfo isolation_info_for_network =
+ cache()->GetIsolationInfoForEndpoint(network_endpoint);
+ EXPECT_EQ(isolation_info_for_network.request_type(),
+ IsolationInfo::RequestType::kOther);
+ EXPECT_EQ(isolation_info_for_network.network_isolation_key(),
+ network_endpoint.group_key.network_isolation_key);
+ EXPECT_TRUE(isolation_info_for_network.site_for_cookies().IsNull());
+}
+
INSTANTIATE_TEST_SUITE_P(ReportingCacheStoreTest,
ReportingCacheTest,
testing::Bool());
diff --git a/net/reporting/reporting_delivery_agent.cc b/net/reporting/reporting_delivery_agent.cc
index 4161ab9..6b92f1f 100644
--- a/net/reporting/reporting_delivery_agent.cc
+++ b/net/reporting/reporting_delivery_agent.cc
@@ -19,6 +19,7 @@
#include "base/time/tick_clock.h"
#include "base/timer/timer.h"
#include "base/values.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/base/url_util.h"
#include "net/reporting/reporting_cache.h"
@@ -82,11 +83,11 @@
// report origin and reporting source are the same, and they all get assigned
// to the same endpoint URL.
struct Target {
- Target(const NetworkIsolationKey& network_isolation_key,
+ Target(const IsolationInfo& isolation_info,
const url::Origin& origin,
const GURL& endpoint_url,
const absl::optional<base::UnguessableToken> reporting_source)
- : network_isolation_key(network_isolation_key),
+ : isolation_info(isolation_info),
origin(origin),
endpoint_url(endpoint_url),
reporting_source(reporting_source) {}
@@ -94,13 +95,16 @@
~Target() = default;
bool operator<(const Target& other) const {
- return std::tie(network_isolation_key, origin, endpoint_url,
- reporting_source) <
- std::tie(other.network_isolation_key, other.origin,
- other.endpoint_url, other.reporting_source);
+ // Note that sorting by NIK here is required for V0 reports; V1 reports
+ // should not need this (but it doesn't hurt). We can remove that as a
+ // comparison key when V0 reporting endpoints are removed.
+ return std::tie(isolation_info.network_isolation_key(), origin,
+ endpoint_url, reporting_source) <
+ std::tie(other.isolation_info.network_isolation_key(),
+ other.origin, other.endpoint_url, other.reporting_source);
}
- NetworkIsolationKey network_isolation_key;
+ IsolationInfo isolation_info;
url::Origin origin;
GURL endpoint_url;
absl::optional<base::UnguessableToken> reporting_source;
@@ -161,7 +165,7 @@
}
const NetworkIsolationKey& network_isolation_key() const {
- return target_.network_isolation_key;
+ return target_.isolation_info.network_isolation_key();
}
const GURL& endpoint_url() const { return target_.endpoint_url; }
const ReportList& reports() const { return reports_; }
@@ -299,10 +303,13 @@
pending_groups_.insert(report_group_key);
+ IsolationInfo isolation_info =
+ cache()->GetIsolationInfoForEndpoint(endpoint);
+
// Add the reports to the appropriate delivery.
- Delivery::Target target(report_group_key.network_isolation_key,
- report_group_key.origin, endpoint.info.url,
- report_group_key.reporting_source);
+ Delivery::Target target(isolation_info, report_group_key.origin,
+ endpoint.info.url,
+ endpoint.group_key.reporting_source);
auto delivery_it = deliveries.find(target);
if (delivery_it == deliveries.end()) {
bool inserted;
@@ -335,8 +342,9 @@
// TODO: Calculate actual max depth.
uploader()->StartUpload(
- target.origin, target.endpoint_url, target.network_isolation_key,
+ target.origin, target.endpoint_url, target.isolation_info,
upload_data, max_depth,
+ /*eligible_for_credentials=*/target.reporting_source.has_value(),
base::BindOnce(&ReportingDeliveryAgentImpl::OnUploadComplete,
weak_factory_.GetWeakPtr(), std::move(delivery)));
}
diff --git a/net/reporting/reporting_delivery_agent_unittest.cc b/net/reporting/reporting_delivery_agent_unittest.cc
index 6f734a1f..1000959 100644
--- a/net/reporting/reporting_delivery_agent_unittest.cc
+++ b/net/reporting/reporting_delivery_agent_unittest.cc
@@ -17,6 +17,7 @@
#include "base/values.h"
#include "net/base/backoff_entry.h"
#include "net/base/features.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/base/schemeful_site.h"
#include "net/reporting/reporting_cache.h"
@@ -88,10 +89,10 @@
// has matching reporting_source.
void UploadFirstDocumentReportAndStartTimer() {
ReportingEndpointGroupKey dummy_group(
- NetworkIsolationKey(), kDocumentReportingSource_,
+ kNik_, kDocumentReportingSource_,
url::Origin::Create(GURL("https://dummy.test")), "dummy");
SetV1EndpointInCache(dummy_group, kDocumentReportingSource_,
- GURL("https://dummy.test/upload"));
+ kIsolationInfo_, GURL("https://dummy.test/upload"));
AddReport(kDocumentReportingSource_, dummy_group.network_isolation_key,
dummy_group.origin.GetURL(), dummy_group.group_name);
@@ -123,6 +124,16 @@
const NetworkIsolationKey kOtherNik_ =
NetworkIsolationKey(SchemefulSite(kOtherOrigin_),
SchemefulSite(kOtherOrigin_));
+ const IsolationInfo kIsolationInfo_ =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther,
+ kOrigin_,
+ kOrigin_,
+ SiteForCookies::FromOrigin(kOrigin_));
+ const IsolationInfo kOtherIsolationInfo_ =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther,
+ kOtherOrigin_,
+ kOtherOrigin_,
+ SiteForCookies::FromOrigin(kOtherOrigin_));
const GURL kEndpoint_ = GURL("https://endpoint/");
const std::string kUserAgent_ = "Mozilla/1.0";
const std::string kGroup_ = "group";
@@ -186,11 +197,33 @@
// TODO(dcreager): Check that BackoffEntry was informed of success.
}
+TEST_F(ReportingDeliveryAgentTest, ReportToHeaderCountedCorrectly) {
+ base::HistogramTester histograms;
+
+ // Set an endpoint with no reporting source (as if configured with the
+ // Report-To header).
+ ASSERT_TRUE(SetEndpointInCache(kGroupKey_, kEndpoint_, kExpires_));
+
+ // Add and upload a report with an associated source.
+ AddReport(kDocumentReportingSource_, kNik_, kUrl_, kGroup_);
+ pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+
+ // Successful upload should count this as a Report-To delivery, even though
+ // the report itself had a reporting source.
+ histograms.ExpectBucketCount(
+ kReportingUploadHeaderTypeHistogram,
+ ReportingDeliveryAgent::ReportingUploadHeaderType::kReportTo, 1);
+ histograms.ExpectBucketCount(
+ kReportingUploadHeaderTypeHistogram,
+ ReportingDeliveryAgent::ReportingUploadHeaderType::kReportingEndpoints,
+ 0);
+}
+
TEST_F(ReportingDeliveryAgentTest, SuccessfulImmediateUploadDocumentReport) {
base::HistogramTester histograms;
SetV1EndpointInCache(kDocumentGroupKey_, kDocumentReportingSource_,
- kEndpoint_);
+ kIsolationInfo_, kEndpoint_);
AddReport(kDocumentReportingSource_, kNik_, kUrl_, kGroup_);
// Upload is automatically started when cache is modified.
@@ -242,7 +275,7 @@
base::HistogramTester histograms;
SetV1EndpointInCache(kDocumentGroupKey_, kDocumentReportingSource_,
- kEndpoint_);
+ kIsolationInfo_, kEndpoint_);
AddReport(kDocumentReportingSource_, kNik_, kUrl_, kGroup_);
AddReport(kDocumentReportingSource_, kNik_, kUrl_, kGroup_);
@@ -767,6 +800,16 @@
const base::UnguessableToken kReportingSource3 =
base::UnguessableToken::Create();
+ const IsolationInfo kIsolationInfo1 =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther, kOrigin_,
+ kOrigin_, SiteForCookies::FromOrigin(kOrigin_));
+ const IsolationInfo kIsolationInfo2 =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther, kOrigin_,
+ kOrigin_, SiteForCookies::FromOrigin(kOrigin_));
+ const IsolationInfo kIsolationInfo3 = IsolationInfo::Create(
+ IsolationInfo::RequestType::kOther, kOtherOrigin_, kOtherOrigin_,
+ SiteForCookies::FromOrigin(kOtherOrigin_));
+
// Set up identical endpoint configuration for kReportingSource1 and
// kReportingSource2. kReportingSource3 is independent.
const ReportingEndpointGroupKey kGroup1Key1(kNik_, kReportingSource1,
@@ -780,11 +823,12 @@
const ReportingEndpointGroupKey kOtherGroupKey(kOtherNik_, kReportingSource3,
kOtherOrigin_, kGroup_);
- SetV1EndpointInCache(kGroup1Key1, kReportingSource1, kUrl_);
- SetV1EndpointInCache(kGroup2Key1, kReportingSource1, kUrl_);
- SetV1EndpointInCache(kGroup1Key2, kReportingSource2, kUrl_);
- SetV1EndpointInCache(kGroup2Key2, kReportingSource2, kUrl_);
- SetV1EndpointInCache(kOtherGroupKey, kReportingSource3, kOtherUrl_);
+ SetV1EndpointInCache(kGroup1Key1, kReportingSource1, kIsolationInfo1, kUrl_);
+ SetV1EndpointInCache(kGroup2Key1, kReportingSource1, kIsolationInfo1, kUrl_);
+ SetV1EndpointInCache(kGroup1Key2, kReportingSource2, kIsolationInfo2, kUrl_);
+ SetV1EndpointInCache(kGroup2Key2, kReportingSource2, kIsolationInfo2, kUrl_);
+ SetV1EndpointInCache(kOtherGroupKey, kReportingSource3, kIsolationInfo3,
+ kOtherUrl_);
UploadFirstReportAndStartTimer();
@@ -826,6 +870,16 @@
const base::UnguessableToken kReportingSource3 =
base::UnguessableToken::Create();
+ const IsolationInfo kIsolationInfo1 =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther, kOrigin_,
+ kOrigin_, SiteForCookies::FromOrigin(kOrigin_));
+ const IsolationInfo kIsolationInfo2 =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther, kOrigin_,
+ kOrigin_, SiteForCookies::FromOrigin(kOrigin_));
+ const IsolationInfo kIsolationInfo3 = IsolationInfo::Create(
+ IsolationInfo::RequestType::kOther, kOtherOrigin_, kOtherOrigin_,
+ SiteForCookies::FromOrigin(kOtherOrigin_));
+
// Set up identical endpoint configuration for kReportingSource1 and
// kReportingSource2. kReportingSource3 is independent.
const ReportingEndpointGroupKey kGroup1Key1(kNik_, kReportingSource1,
@@ -839,11 +893,12 @@
const ReportingEndpointGroupKey kOtherGroupKey(kOtherNik_, kReportingSource3,
kOtherOrigin_, kGroup_);
- SetV1EndpointInCache(kGroup1Key1, kReportingSource1, kUrl_);
- SetV1EndpointInCache(kGroup2Key1, kReportingSource1, kUrl_);
- SetV1EndpointInCache(kGroup1Key2, kReportingSource2, kUrl_);
- SetV1EndpointInCache(kGroup2Key2, kReportingSource2, kUrl_);
- SetV1EndpointInCache(kOtherGroupKey, kReportingSource3, kOtherUrl_);
+ SetV1EndpointInCache(kGroup1Key1, kReportingSource1, kIsolationInfo1, kUrl_);
+ SetV1EndpointInCache(kGroup2Key1, kReportingSource1, kIsolationInfo1, kUrl_);
+ SetV1EndpointInCache(kGroup1Key2, kReportingSource2, kIsolationInfo2, kUrl_);
+ SetV1EndpointInCache(kGroup2Key2, kReportingSource2, kIsolationInfo2, kUrl_);
+ SetV1EndpointInCache(kOtherGroupKey, kReportingSource3, kIsolationInfo3,
+ kOtherUrl_);
UploadFirstReportAndStartTimer();
diff --git a/net/reporting/reporting_endpoint_manager_unittest.cc b/net/reporting/reporting_endpoint_manager_unittest.cc
index 2b5254e..dee77fe 100644
--- a/net/reporting/reporting_endpoint_manager_unittest.cc
+++ b/net/reporting/reporting_endpoint_manager_unittest.cc
@@ -11,6 +11,7 @@
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "net/base/backoff_entry.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/base/schemeful_site.h"
#include "net/reporting/reporting_cache.h"
@@ -140,6 +141,7 @@
}
void OnParsedReportingEndpointsHeader(
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
std::vector<ReportingEndpoint> endpoints) override {
NOTREACHED();
}
@@ -224,9 +226,15 @@
}
void SetV1EndpointForTesting(const ReportingEndpointGroupKey& group_key,
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
const GURL& url) override {
NOTREACHED();
}
+ IsolationInfo GetIsolationInfoForEndpoint(
+ const ReportingEndpoint& endpoint) const override {
+ NOTREACHED();
+ return IsolationInfo();
+ }
private:
const url::Origin expected_origin_;
diff --git a/net/reporting/reporting_garbage_collector_unittest.cc b/net/reporting/reporting_garbage_collector_unittest.cc
index 79854c3a..aa27ee9 100644
--- a/net/reporting/reporting_garbage_collector_unittest.cc
+++ b/net/reporting/reporting_garbage_collector_unittest.cc
@@ -9,6 +9,8 @@
#include "base/test/simple_test_tick_clock.h"
#include "base/time/time.h"
#include "base/timer/mock_timer.h"
+#include "net/base/isolation_info.h"
+#include "net/base/network_isolation_key.h"
#include "net/reporting/reporting_cache.h"
#include "net/reporting/reporting_policy.h"
#include "net/reporting/reporting_report.h"
@@ -30,6 +32,7 @@
const absl::optional<base::UnguessableToken> kReportingSource_ =
base::UnguessableToken::Create();
const NetworkIsolationKey kNik_;
+ const IsolationInfo kIsolationInfo_;
const GURL kUrl_ = GURL("https://origin/path");
const std::string kUserAgent_ = "Mozilla/1.0";
const std::string kGroup_ = "group";
@@ -94,7 +97,8 @@
TEST_F(ReportingGarbageCollectorTest, ExpiredSource) {
ReportingEndpointGroupKey group_key(kNik_, kReportingSource_,
url::Origin::Create(kUrl_), kGroup_);
- cache()->SetV1EndpointForTesting(group_key, *kReportingSource_, kUrl_);
+ cache()->SetV1EndpointForTesting(group_key, *kReportingSource_,
+ kIsolationInfo_, kUrl_);
// Mark the source as expired. The source should be removed as soon as
// garbage collection runs, as there are no queued reports for it.
@@ -113,7 +117,8 @@
TEST_F(ReportingGarbageCollectorTest, ExpiredSourceWithPendingReports) {
ReportingEndpointGroupKey group_key(kNik_, kReportingSource_,
url::Origin::Create(kUrl_), kGroup_);
- cache()->SetV1EndpointForTesting(group_key, *kReportingSource_, kUrl_);
+ cache()->SetV1EndpointForTesting(group_key, *kReportingSource_,
+ kIsolationInfo_, kUrl_);
cache()->AddReport(kReportingSource_, kNik_, kUrl_, kUserAgent_, kGroup_,
kType_, std::make_unique<base::DictionaryValue>(), 0,
tick_clock()->NowTicks(), 0);
diff --git a/net/reporting/reporting_header_parser.cc b/net/reporting/reporting_header_parser.cc
index 0764197..d80086d 100644
--- a/net/reporting/reporting_header_parser.cc
+++ b/net/reporting/reporting_header_parser.cc
@@ -17,6 +17,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/features.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/reporting/reporting_cache.h"
@@ -222,14 +223,14 @@
bool ProcessV1Endpoint(ReportingDelegate* delegate,
ReportingCache* cache,
const base::UnguessableToken& reporting_source,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const url::Origin& origin,
const std::string& endpoint_name,
const std::string& endpoint_url_string,
ReportingEndpoint& parsed_endpoint_out) {
DCHECK(!reporting_source.is_empty());
- ReportingEndpointGroupKey group_key(network_isolation_key, reporting_source,
- origin, endpoint_name);
+ ReportingEndpointGroupKey group_key(isolation_info.network_isolation_key(),
+ reporting_source, origin, endpoint_name);
parsed_endpoint_out.group_key = group_key;
ReportingEndpoint::EndpointInfo parsed_endpoint;
@@ -324,7 +325,7 @@
void ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
ReportingContext* context,
const base::UnguessableToken& reporting_source,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const url::Origin& origin,
base::flat_map<std::string, std::string> header) {
DCHECK(base::FeatureList::IsEnabled(net::features::kDocumentReporting));
@@ -338,9 +339,9 @@
for (const auto& member : header) {
ReportingEndpoint parsed_endpoint;
- if (ProcessV1Endpoint(delegate, cache, reporting_source,
- network_isolation_key, origin, member.first,
- member.second, parsed_endpoint)) {
+ if (ProcessV1Endpoint(delegate, cache, reporting_source, isolation_info,
+ origin, member.first, member.second,
+ parsed_endpoint)) {
parsed_header.push_back(std::move(parsed_endpoint));
}
}
@@ -351,7 +352,7 @@
}
RecordReportingHeaderType(ReportingHeaderType::kReportingEndpoints);
- cache->OnParsedReportingEndpointsHeader(reporting_source,
+ cache->OnParsedReportingEndpointsHeader(reporting_source, isolation_info,
std::move(parsed_header));
}
diff --git a/net/reporting/reporting_header_parser.h b/net/reporting/reporting_header_parser.h
index 71a7101..5db263e 100644
--- a/net/reporting/reporting_header_parser.h
+++ b/net/reporting/reporting_header_parser.h
@@ -21,6 +21,7 @@
namespace net {
+class IsolationInfo;
class NetworkIsolationKey;
class ReportingContext;
@@ -58,7 +59,7 @@
static void ProcessParsedReportingEndpointsHeader(
ReportingContext* context,
const base::UnguessableToken& reporting_source,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const url::Origin& origin,
base::flat_map<std::string, std::string> parsed_header);
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc
index 2be6544..ec2023d 100644
--- a/net/reporting/reporting_header_parser_unittest.cc
+++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -18,6 +18,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/features.h"
+#include "net/base/isolation_info.h"
#include "net/base/schemeful_site.h"
#include "net/reporting/mock_persistent_reporting_store.h"
#include "net/reporting/reporting_cache.h"
@@ -74,6 +75,11 @@
NetworkIsolationKey(SchemefulSite(kOrigin1_), SchemefulSite(kOrigin1_));
const NetworkIsolationKey kOtherNik_ =
NetworkIsolationKey(SchemefulSite(kOrigin2_), SchemefulSite(kOrigin2_));
+ const IsolationInfo kIsolationInfo_ =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther,
+ kOrigin1_,
+ kOrigin1_,
+ SiteForCookies::FromOrigin(kOrigin1_));
const GURL kUrlEtld_ = GURL("https://co.uk/foo.html/");
const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_);
const GURL kEndpoint1_ = GURL("https://endpoint1.test/");
@@ -1771,7 +1777,7 @@
}
void ParseHeader(const base::UnguessableToken& reporting_source,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const url::Origin& origin,
const std::string& header_string) {
absl::optional<base::flat_map<std::string, std::string>> header_map =
@@ -1779,19 +1785,17 @@
if (header_map) {
ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
- context(), reporting_source, network_isolation_key, origin,
- *header_map);
+ context(), reporting_source, isolation_info, origin, *header_map);
}
}
void ProcessParsedHeader(
const base::UnguessableToken& reporting_source,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const url::Origin& origin,
const absl::optional<base::flat_map<std::string, std::string>>&
header_map) {
ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
- context(), reporting_source, network_isolation_key, origin,
- *header_map);
+ context(), reporting_source, isolation_info, origin, *header_map);
}
const base::UnguessableToken kReportingSource_ =
@@ -1835,7 +1839,8 @@
<< "Syntactically valid Reporting-Endpoints header (\""
<< test_case.description << ": \"" << test_case.header_value
<< "\") parsed as invalid.";
- ProcessParsedHeader(kReportingSource_, kNik_, kOrigin1_, parsed_result);
+ ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
+ parsed_result);
invalid_case_count++;
histograms.ExpectBucketCount(
@@ -1874,7 +1879,8 @@
std::string header =
ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
auto parsed_result = ParseReportingEndpoints(header);
- ProcessParsedHeader(kReportingSource_, kNik_, kOrigin1_, parsed_result);
+ ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
+ parsed_result);
// Ensure that the endpoint was not inserted into the persistent endpoint
// groups used for v0 reporting.
@@ -1884,6 +1890,9 @@
cache()->GetV1EndpointForTesting(kReportingSource_, kGroup1_);
EXPECT_TRUE(endpoint);
+ IsolationInfo isolation_info = cache()->GetIsolationInfoForEndpoint(endpoint);
+ EXPECT_TRUE(isolation_info.IsEqualForTesting(kIsolationInfo_));
+
EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
EXPECT_EQ(kEndpoint1_, endpoint.info.url);
@@ -1907,7 +1916,8 @@
base::HistogramTester histograms;
std::string header = "group1=\"/path-absolute-url\"";
auto parsed_result = ParseReportingEndpoints(header);
- ProcessParsedHeader(kReportingSource_, kNik_, kOrigin1_, parsed_result);
+ ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
+ parsed_result);
// Ensure that the endpoint was not inserted into the persistent endpoint
// groups used for v0 reporting.
diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc
index 58dea43..a6388e22 100644
--- a/net/reporting/reporting_service.cc
+++ b/net/reporting/reporting_service.cc
@@ -16,6 +16,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/features.h"
+#include "net/base/isolation_info.h"
#include "net/http/structured_headers.h"
#include "net/reporting/reporting_browsing_data_remover.h"
#include "net/reporting/reporting_cache.h"
@@ -62,14 +63,13 @@
void SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) override {
DCHECK(!reporting_source.is_empty());
DoOrBacklogTask(
base::BindOnce(&ReportingServiceImpl::DoSetDocumentReportingEndpoints,
- base::Unretained(this), reporting_source,
- FixupNetworkIsolationKey(network_isolation_key), origin,
- std::move(endpoints)));
+ base::Unretained(this), reporting_source, isolation_info,
+ origin, std::move(endpoints)));
}
void SendReportsAndRemoveSource(
@@ -221,12 +221,12 @@
void DoSetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const url::Origin& origin,
base::flat_map<std::string, std::string> header_value) {
DCHECK(initialized_);
ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
- context_.get(), reporting_source, network_isolation_key, origin,
+ context_.get(), reporting_source, isolation_info, origin,
std::move(header_value));
}
diff --git a/net/reporting/reporting_service.h b/net/reporting/reporting_service.h
index 8643e16..68ae61d 100644
--- a/net/reporting/reporting_service.h
+++ b/net/reporting/reporting_service.h
@@ -25,6 +25,7 @@
namespace net {
+class IsolationInfo;
class NetworkIsolationKey;
class ReportingContext;
struct ReportingPolicy;
@@ -90,15 +91,11 @@
// this header was received, and must not be empty.
// |endpoints| is a mapping of endpoint names to URLs.
// |origin| is the origin of the reporting source, and
- // |network_isolation_key| is the appropriate NIK for that source.
- // (The isolation provided by Reporting-Endpoints is stronger than that
- // provided by Report-To, so the origin and NIK aren't strictly necessary,
- // but are currently required here because of shared infrastructure between
- // the two versions of the reporting API.)
+ // |isolation_info| is the appropriate IsolationInfo struct for that source.
virtual void SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) = 0;
// Attempts to send any queued reports and removes all associated
diff --git a/net/reporting/reporting_service_unittest.cc b/net/reporting/reporting_service_unittest.cc
index 510e498..ab563cc 100644
--- a/net/reporting/reporting_service_unittest.cc
+++ b/net/reporting/reporting_service_unittest.cc
@@ -13,6 +13,7 @@
#include "base/time/tick_clock.h"
#include "base/values.h"
#include "net/base/features.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/base/schemeful_site.h"
#include "net/reporting/mock_persistent_reporting_store.h"
@@ -61,6 +62,11 @@
ReportingEndpointGroupKey(kNik_, kOrigin_, kGroup_);
const ReportingEndpointGroupKey kGroupKey2_ =
ReportingEndpointGroupKey(kNik2_, kOrigin2_, kGroup_);
+ const IsolationInfo kIsolationInfo_ =
+ IsolationInfo::Create(IsolationInfo::RequestType::kOther,
+ kOrigin_,
+ kOrigin_,
+ SiteForCookies::FromOrigin(kOrigin_));
ReportingServiceTest() {
feature_list_.InitAndEnableFeature(
@@ -196,8 +202,8 @@
auto parsed_header =
ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
ASSERT_TRUE(parsed_header.has_value());
- service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, kNik_,
- *parsed_header);
+ service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
+ kIsolationInfo_, *parsed_header);
FinishLoading(true /* load_success */);
// Endpoint should not be part of the persistent store.
@@ -214,8 +220,8 @@
ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
ASSERT_TRUE(parsed_header.has_value());
- service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, kNik_,
- *parsed_header);
+ service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
+ kIsolationInfo_, *parsed_header);
// This report should be sent immediately, starting the delivery agent timer.
service()->QueueReport(kUrl_, kReportingSource_, kNik_, kUserAgent_, kGroup_,
kType_, std::make_unique<base::DictionaryValue>(), 0);
@@ -249,8 +255,8 @@
ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
ASSERT_TRUE(parsed_header.has_value());
- service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, kNik_,
- *parsed_header);
+ service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
+ kIsolationInfo_, *parsed_header);
// This report should be sent immediately, starting the delivery agent timer.
service()->QueueReport(kUrl_, kReportingSource_, kNik_, kUserAgent_, kGroup_,
kType_, std::make_unique<base::DictionaryValue>(), 0);
@@ -291,8 +297,8 @@
feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
auto parsed_header = ParseReportingEndpoints(kGroup_ + "=\"/path-absolute\"");
ASSERT_TRUE(parsed_header.has_value());
- service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_, kNik_,
- *parsed_header);
+ service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
+ kIsolationInfo_, *parsed_header);
FinishLoading(true /* load_success */);
// Endpoint should not be part of the persistent store.
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index d3a637a..9f56dfa 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -17,6 +17,7 @@
#include "base/test/simple_test_clock.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/timer/mock_timer.h"
+#include "net/base/isolation_info.h"
#include "net/base/network_isolation_key.h"
#include "net/reporting/reporting_cache.h"
#include "net/reporting/reporting_context.h"
@@ -38,13 +39,13 @@
public:
PendingUploadImpl(const url::Origin& report_origin,
const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const std::string& json,
ReportingUploader::UploadCallback callback,
base::OnceCallback<void(PendingUpload*)> complete_callback)
: report_origin_(report_origin),
url_(url),
- network_isolation_key_(network_isolation_key),
+ isolation_info_(isolation_info),
json_(json),
callback_(std::move(callback)),
complete_callback_(std::move(complete_callback)) {}
@@ -68,7 +69,7 @@
private:
url::Origin report_origin_;
GURL url_;
- NetworkIsolationKey network_isolation_key_;
+ IsolationInfo isolation_info_;
std::string json_;
ReportingUploader::UploadCallback callback_;
base::OnceCallback<void(PendingUpload*)> complete_callback_;
@@ -103,15 +104,15 @@
TestReportingUploader::TestReportingUploader() = default;
TestReportingUploader::~TestReportingUploader() = default;
-void TestReportingUploader::StartUpload(
- const url::Origin& report_origin,
- const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
- const std::string& json,
- int max_depth,
- UploadCallback callback) {
+void TestReportingUploader::StartUpload(const url::Origin& report_origin,
+ const GURL& url,
+ const IsolationInfo& isolation_info,
+ const std::string& json,
+ int max_depth,
+ bool eligible_for_credentials,
+ UploadCallback callback) {
pending_uploads_.push_back(std::make_unique<PendingUploadImpl>(
- report_origin, url, network_isolation_key, json, std::move(callback),
+ report_origin, url, isolation_info, json, std::move(callback),
base::BindOnce(&ErasePendingUpload, &pending_uploads_)));
}
@@ -231,8 +232,10 @@
void ReportingTestBase::SetV1EndpointInCache(
const ReportingEndpointGroupKey& group_key,
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
const GURL& url) {
- cache()->SetV1EndpointForTesting(group_key, reporting_source, url);
+ cache()->SetV1EndpointForTesting(group_key, reporting_source, isolation_info,
+ url);
}
bool ReportingTestBase::EndpointExistsInCache(
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index 2659997..fbf70e0 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -40,7 +40,7 @@
namespace net {
-class NetworkIsolationKey;
+class IsolationInfo;
struct ReportingEndpoint;
class ReportingGarbageCollector;
@@ -89,9 +89,10 @@
void StartUpload(const url::Origin& report_origin,
const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const std::string& json,
int max_depth,
+ bool eligible_for_credentials,
UploadCallback callback) override;
void OnShutdown() override;
@@ -218,6 +219,7 @@
// endpoints map using |reporting_source| as key.
void SetV1EndpointInCache(const ReportingEndpointGroupKey& group_key,
const base::UnguessableToken& reporting_source,
+ const IsolationInfo& isolation_info,
const GURL& url);
// Returns whether an endpoint with the given properties exists in the cache.
@@ -333,7 +335,7 @@
void SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) override {}
void SendReportsAndRemoveSource(
diff --git a/net/reporting/reporting_uploader.cc b/net/reporting/reporting_uploader.cc
index 49c7eac..ed43507 100644
--- a/net/reporting/reporting_uploader.cc
+++ b/net/reporting/reporting_uploader.cc
@@ -81,14 +81,14 @@
PendingUpload(const url::Origin& report_origin,
const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const std::string& json,
int max_depth,
ReportingUploader::UploadCallback callback)
: state(CREATED),
report_origin(report_origin),
url(url),
- network_isolation_key(network_isolation_key),
+ isolation_info(isolation_info),
payload_reader(UploadOwnedBytesElementReader::CreateWithString(json)),
max_depth(max_depth),
callback(std::move(callback)) {}
@@ -100,7 +100,7 @@
State state;
const url::Origin report_origin;
const GURL url;
- const NetworkIsolationKey network_isolation_key;
+ const IsolationInfo isolation_info;
std::unique_ptr<UploadElementReader> payload_reader;
int max_depth;
ReportingUploader::UploadCallback callback;
@@ -122,18 +122,19 @@
void StartUpload(const url::Origin& report_origin,
const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const std::string& json,
int max_depth,
+ bool eligible_for_credentials,
UploadCallback callback) override {
- auto upload = std::make_unique<PendingUpload>(
- report_origin, url, network_isolation_key, json, max_depth,
- std::move(callback));
+ auto upload =
+ std::make_unique<PendingUpload>(report_origin, url, isolation_info,
+ json, max_depth, std::move(callback));
auto collector_origin = url::Origin::Create(url);
if (collector_origin == report_origin) {
// Skip the preflight check if the reports are being sent to the same
// origin as the requests they describe.
- StartPayloadRequest(std::move(upload));
+ StartPayloadRequest(std::move(upload), eligible_for_credentials);
} else {
StartPreflightRequest(std::move(upload));
}
@@ -155,8 +156,7 @@
upload->request->SetLoadFlags(LOAD_DISABLE_CACHE);
upload->request->set_allow_credentials(false);
- upload->request->set_isolation_info(IsolationInfo::CreatePartial(
- IsolationInfo::RequestType::kOther, upload->network_isolation_key));
+ upload->request->set_isolation_info(upload->isolation_info);
upload->request->SetExtraRequestHeaderByName(
HttpRequestHeaders::kOrigin, upload->report_origin.Serialize(), true);
@@ -176,7 +176,8 @@
raw_request->Start();
}
- void StartPayloadRequest(std::unique_ptr<PendingUpload> upload) {
+ void StartPayloadRequest(std::unique_ptr<PendingUpload> upload,
+ bool eligible_for_credentials) {
DCHECK(upload->state == PendingUpload::CREATED ||
upload->state == PendingUpload::SENDING_PREFLIGHT);
@@ -186,9 +187,19 @@
upload->request->set_method("POST");
upload->request->SetLoadFlags(LOAD_DISABLE_CACHE);
- upload->request->set_allow_credentials(false);
- upload->request->set_isolation_info(IsolationInfo::CreatePartial(
- IsolationInfo::RequestType::kOther, upload->network_isolation_key));
+
+ // Credentials are sent for V1 reports, if the endpoint is same-origin with
+ // the site generating the report (this will be set to false either by the
+ // delivery agent determining that this is a V0 report, or by `StartUpload`
+ // determining that this is a cross-origin case, and taking the CORS
+ // preflight path).
+ upload->request->set_allow_credentials(eligible_for_credentials);
+ // The site for cookies is taken from the reporting source's IsolationInfo,
+ // in the case of V1 reporting endpoints, and will be null for V0 reports.
+ upload->request->set_site_for_cookies(
+ upload->isolation_info.site_for_cookies());
+ upload->request->set_initiator(upload->isolation_info.frame_origin());
+ upload->request->set_isolation_info(upload->isolation_info);
upload->request->SetExtraRequestHeaderByName(
HttpRequestHeaders::kContentType, kUploadContentType, true);
@@ -286,8 +297,9 @@
upload->RunCallback(ReportingUploader::Outcome::FAILURE);
return;
}
-
- StartPayloadRequest(std::move(upload));
+ // Any upload which required CORS should not receive credentials, as they
+ // are sent to same-origin endpoints only.
+ StartPayloadRequest(std::move(upload), /*eligible_for_credentials=*/false);
}
void HandlePayloadResponse(std::unique_ptr<PendingUpload> upload,
diff --git a/net/reporting/reporting_uploader.h b/net/reporting/reporting_uploader.h
index 9fa0318..7749cd54e 100644
--- a/net/reporting/reporting_uploader.h
+++ b/net/reporting/reporting_uploader.h
@@ -19,7 +19,7 @@
namespace net {
-class NetworkIsolationKey;
+class IsolationInfo;
class URLRequestContext;
// Uploads already-serialized reports and converts responses to one of the
@@ -35,12 +35,14 @@
// Starts to upload the reports in |json| (properly tagged as JSON data) to
// |url|, and calls |callback| when complete (whether successful or not).
// All of the reports in |json| must describe requests to the same origin;
- // |report_origin| must be that origin.
+ // |report_origin| must be that origin. Credentials may be sent with the
+ // upload if |eligible_for_credentials| is true.
virtual void StartUpload(const url::Origin& report_origin,
const GURL& url,
- const NetworkIsolationKey& network_isolation_key,
+ const IsolationInfo& isolation_info,
const std::string& json,
int max_depth,
+ bool eligible_for_credentials,
UploadCallback callback) = 0;
// Cancels pending uploads.
diff --git a/net/reporting/reporting_uploader_unittest.cc b/net/reporting/reporting_uploader_unittest.cc
index 3f677cc..53bd470f 100644
--- a/net/reporting/reporting_uploader_unittest.cc
+++ b/net/reporting/reporting_uploader_unittest.cc
@@ -140,8 +140,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
}
@@ -151,8 +152,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback.outcome());
@@ -164,8 +166,8 @@
ASSERT_TRUE(server_.ShutdownAndWaitUntilComplete());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, url, NetworkIsolationKey(), kUploadBody, 0,
- callback.callback());
+ uploader_->StartUpload(kOrigin, url, IsolationInfo::CreateTransient(),
+ kUploadBody, 0, false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -177,8 +179,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -191,8 +194,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -216,8 +220,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_TRUE(preflight_received);
@@ -234,8 +239,8 @@
TestUploadCallback callback;
auto server_origin = url::Origin::Create(server_.base_url());
uploader_->StartUpload(server_origin, server_.GetURL("/"),
- NetworkIsolationKey(), kUploadBody, 0,
- callback.callback());
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_FALSE(preflight_received);
@@ -260,8 +265,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -290,8 +296,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -320,8 +327,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -350,8 +358,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::FAILURE, callback.outcome());
@@ -364,8 +373,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::REMOVE_ENDPOINT, callback.outcome());
@@ -408,8 +418,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_TRUE(followed);
@@ -431,8 +442,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
EXPECT_FALSE(followed);
@@ -462,8 +474,9 @@
ASSERT_TRUE(cookie_callback.result().status.IsInclude());
TestUploadCallback upload_callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, upload_callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, upload_callback.callback());
upload_callback.WaitForCall();
}
@@ -483,8 +496,9 @@
ASSERT_TRUE(server_.Start());
TestUploadCallback upload_callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, upload_callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, upload_callback.callback());
upload_callback.WaitForCall();
GetCookieListCallback cookie_callback;
@@ -523,16 +537,18 @@
{
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
}
EXPECT_EQ(1, request_count);
{
TestUploadCallback callback;
- uploader_->StartUpload(kOrigin, server_.GetURL("/"), NetworkIsolationKey(),
- kUploadBody, 0, callback.callback());
+ uploader_->StartUpload(kOrigin, server_.GetURL("/"),
+ IsolationInfo::CreateTransient(), kUploadBody, 0,
+ false, callback.callback());
callback.WaitForCall();
}
EXPECT_EQ(2, request_count);
@@ -556,6 +572,10 @@
ASSERT_NE(kSite1, kSite2);
const NetworkIsolationKey kNetworkIsolationKey1(kSite1, kSite1);
const NetworkIsolationKey kNetworkIsolationKey2(kSite2, kSite2);
+ const IsolationInfo kIsolationInfo1 = IsolationInfo::CreatePartial(
+ IsolationInfo::RequestType::kOther, kNetworkIsolationKey1);
+ const IsolationInfo kIsolationInfo2 = IsolationInfo::CreatePartial(
+ IsolationInfo::RequestType::kOther, kNetworkIsolationKey2);
MockClientSocketFactory socket_factory;
TestURLRequestContext context(true /* delay_initialization */);
@@ -627,9 +647,8 @@
TestUploadCallback callback1;
std::unique_ptr<ReportingUploader> uploader1 =
ReportingUploader::Create(&context);
- uploader1->StartUpload(kOrigin, GURL("https://origin/1"),
- kNetworkIsolationKey1, kUploadBody, 0,
- callback1.callback());
+ uploader1->StartUpload(kOrigin, GURL("https://origin/1"), kIsolationInfo1,
+ kUploadBody, 0, false, callback1.callback());
callback1.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback1.outcome());
@@ -641,15 +660,13 @@
TestUploadCallback callback2;
std::unique_ptr<ReportingUploader> uploader2 =
ReportingUploader::Create(&context);
- uploader2->StartUpload(kOrigin, GURL("https://origin/2"),
- kNetworkIsolationKey2, kUploadBody, 0,
- callback2.callback());
+ uploader2->StartUpload(kOrigin, GURL("https://origin/2"), kIsolationInfo2,
+ kUploadBody, 0, false, callback2.callback());
TestUploadCallback callback3;
std::unique_ptr<ReportingUploader> uploader3 =
ReportingUploader::Create(&context);
- uploader3->StartUpload(kOrigin, GURL("https://origin/3"),
- kNetworkIsolationKey1, kUploadBody, 0,
- callback3.callback());
+ uploader3->StartUpload(kOrigin, GURL("https://origin/3"), kIsolationInfo1,
+ kUploadBody, 0, false, callback3.callback());
callback2.WaitForCall();
EXPECT_EQ(ReportingUploader::Outcome::SUCCESS, callback2.outcome());
diff --git a/net/url_request/url_request_context_builder_unittest.cc b/net/url_request/url_request_context_builder_unittest.cc
index a4a694b..90fbdaae 100644
--- a/net/url_request/url_request_context_builder_unittest.cc
+++ b/net/url_request/url_request_context_builder_unittest.cc
@@ -206,8 +206,9 @@
// Queue a pending upload.
GURL url("https://www.foo.test");
context->reporting_service()->GetContextForTesting()->uploader()->StartUpload(
- url::Origin::Create(url), url, NetworkIsolationKey(), "report body", 0,
- base::DoNothing());
+ url::Origin::Create(url), url, IsolationInfo::CreateTransient(),
+ "report body", 0,
+ /*eligible_for_credentials=*/false, base::DoNothing());
base::RunLoop().RunUntilIdle();
ASSERT_EQ(1, context->reporting_service()
->GetContextForTesting()
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 2dda0e7..f8fddf1 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -43,6 +43,7 @@
#include "crypto/sha2.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/base/features.h"
+#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/network_delegate.h"
@@ -954,14 +955,16 @@
void NetworkContext::SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const net::IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) {
DCHECK(!reporting_source.is_empty());
+ DCHECK_EQ(net::IsolationInfo::RequestType::kOther,
+ isolation_info.request_type());
net::ReportingService* reporting_service =
url_request_context()->reporting_service();
if (reporting_service) {
- reporting_service->SetDocumentReportingEndpoints(
- reporting_source, origin, network_isolation_key, endpoints);
+ reporting_service->SetDocumentReportingEndpoints(reporting_source, origin,
+ isolation_info, endpoints);
}
}
@@ -1111,7 +1114,7 @@
void NetworkContext::SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const net::IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) {
NOTREACHED();
}
diff --git a/services/network/network_context.h b/services/network/network_context.h
index 45974ba..9f20450 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -414,7 +414,7 @@
void SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const net::IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) override;
void SendReportsAndRemoveSource(
const base::UnguessableToken& reporting_source) override;
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 6d2ab19..f3f625c 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -941,18 +941,19 @@
// |reporting_source| is a token which identifies the document or worker with
// which the header was received. The endpoints configured by this method are
// only used to deliver reports which are queued by that same source.
- // |network_isolation_key| is used when creating the endpoints in the
- // ReportingCache, and will need to match the key sent when reports are queued
- // in order for these reporting endpoints to be considered for delivery,
- // although this should always be the case when reports are queued by the
- // same source which configured the endpoints.
+ // |isolation_info| is used when creating the endpoints in the ReportingCache
+ // and when determining which credentials to send with uploaded reports. Its
+ // network isolation key will need to match the key sent when reports are
+ // queued, in order for these reporting endpoints to be considered for
+ // delivery, although this should always be the case when reports are queued
+ // by the same source which configured the endpoints.
// |endpoints| is a mapping of endpoint name to URL (URLs here are represented
// as strings, and will be rejected if they fail to parse or are not secure).
//
// Spec: https://w3c.github.io/reporting/#header
SetDocumentReportingEndpoints(
mojo_base.mojom.UnguessableToken reporting_source,
- url.mojom.Origin origin, NetworkIsolationKey network_isolation_key,
+ url.mojom.Origin origin, IsolationInfo isolation_info,
map<string,string> endpoints);
// Queues any outstanding reports for a single |reporting_source| (which
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index f1fbbf1..e41a44de 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -115,7 +115,7 @@
void SetDocumentReportingEndpoints(
const base::UnguessableToken& reporting_source,
const url::Origin& origin,
- const net::NetworkIsolationKey& network_isolation_key,
+ const net::IsolationInfo& isolation_info,
const base::flat_map<std::string, std::string>& endpoints) override {}
void SendReportsAndRemoveSource(
const base::UnguessableToken& reporting_source) override {}
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 9f37aa04..634c5d3 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6856,9 +6856,6 @@
crbug.com/1241860 http/tests/inspector-protocol/issues/cors-issues.js [ Skip ]
crbug.com/1241860 http/tests/inspector-protocol/issues/renderer-cors-issues.js [ Skip ]
-# Credentials in same-origin Reporting-API reports are currently not implemented.
-crbug.com/1163645 external/wpt/reporting/same-origin-report-credentials.https.sub.html [ Failure ]
-
# [CompositeClipPathAnimations] failing test
crbug.com/1249071 virtual/composite-clip-path-animation/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-filter.html [ Crash Failure Pass ]
diff --git a/third_party/blink/web_tests/external/wpt/cookies/resources/setSameSiteDomain.py b/third_party/blink/web_tests/external/wpt/cookies/resources/setSameSiteDomain.py
new file mode 100644
index 0000000..c8b7a71
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookies/resources/setSameSiteDomain.py
@@ -0,0 +1,36 @@
+from cookies.resources.helpers import makeCookieHeader, setNoCacheAndCORSHeaders
+
+from wptserve.utils import isomorphic_encode
+
+def main(request, response):
+ """Respond to `/cookie/set/samesite?{value}` by setting four cookies:
+ 1. `samesite_strict={value};SameSite=Strict;path=/;domain={host}`
+ 2. `samesite_lax={value};SameSite=Lax;path=/;domain={host}`
+ 3. `samesite_none={value};SameSite=None;path=/;Secure;domain={host}`
+ 4. `samesite_unspecified={value};path=/;domain={host}`
+ Where {host} is the hostname from which this page is served. (Requesting this resource
+ without a Host header will result in a 500 server error.)
+ Then navigate to a page that will post a message back to the opener with the set cookies"""
+ headers = setNoCacheAndCORSHeaders(request, response)
+ value = isomorphic_encode(request.url_parts.query)
+ host_header = request.headers['host']
+ hostname = host_header.split(b":")[0]
+ host = isomorphic_encode(hostname)
+ headers.append((b"Content-Type", b"text/html; charset=utf-8"))
+ headers.append(makeCookieHeader(b"samesite_strict", value, {b"SameSite":b"Strict", b"path":b"/", b"domain":host}))
+ headers.append(makeCookieHeader(b"samesite_lax", value, {b"SameSite":b"Lax", b"path":b"/", b"domain":host}))
+ # SameSite=None cookies must be Secure.
+ headers.append(makeCookieHeader(b"samesite_none", value, {b"SameSite":b"None", b"path":b"/", b"Secure": b"", b"domain":host}))
+ headers.append(makeCookieHeader(b"samesite_unspecified", value, {b"path":b"/", b"domain":host}))
+
+ document = b"""
+<!DOCTYPE html>
+<script>
+ // A same-site navigation, which should attach all cookies including SameSite ones.
+ // This is necessary because this page may have been reached via a cross-site navigation, so
+ // we might not have access to some SameSite cookies from here.
+ window.location = "../samesite/resources/echo-cookies.html";
+</script>
+"""
+
+ return headers, document
diff --git a/third_party/blink/web_tests/external/wpt/reporting/cross-origin-same-site-credentials.https.sub.html b/third_party/blink/web_tests/external/wpt/reporting/cross-origin-same-site-credentials.https.sub.html
new file mode 100644
index 0000000..2f3f5fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/cross-origin-same-site-credentials.https.sub.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that credentials are sent properly in a cross-origin but same-site nested context</title>
+ <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+ <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+ <script src='resources/report-helper.js'></script>
+</head>
+<body>
+ <script>
+ const base_url = `${location.protocol}//${location.host}`;
+ const endpoint = `${base_url}/reporting/resources/report.py`;
+ const id = 'd0d517bf-891b-457a-b970-8b2b2c81a0bf';
+
+ promise_test(async t => {
+ // If this is not run from the expected origin, then the A->A->www.A frame embedding will not be correct,
+ // and the cookies set in the top-level page will never be returned with the reports.
+ assert_true(location.href.startsWith("https://{{hosts[][]}}:{{ports[https][0]}}/"),
+ "Test running on unexpected origin; subsequent assertions will fail.");
+
+ // Set credentials, and set up test to clear them afterwards. Cookies are set with the Domain
+ // attribute, so that they may be sent to same-site resources.
+ await fetch('/cookies/resources/setSameSiteDomain.py?reporting', {mode: 'no-cors', credentials: 'include', cache: 'no-store'});
+ t.add_cleanup(() => fetch("/cookies/resources/dropSameSite.py", {mode: 'no-cors', credentials: 'include', cache: 'no-store'}));
+
+ // Insert a same-origin frame, which will then frame a same-site but cross-origin page to
+ // trigger a CSP error.
+ const frame = document.createElement('iframe');
+ frame.src = "https://{{hosts[][]}}:{{ports[https][0]}}/reporting/resources/middle-frame.https.sub.html?host={{hosts[][www]}}";
+
+ // Wait for the inner frame to signal that the report has been generated.
+ await new Promise(resolve => {
+ window.addEventListener('message', ev => {
+ if (ev.data === "done")
+ resolve(ev.data);
+ });
+ document.body.appendChild(frame);
+ });
+
+ const reports = await pollReports(endpoint, id);
+ checkReportExists(reports, 'csp-violation', "https://{{hosts[][www]}}:{{ports[https][0]}}/reporting/resources/same-origin-report.https.sub.html");
+
+ // All credentials set at the top-level should be received.
+ const cookies = await pollCookies(endpoint, id);
+ assert_equals(cookies.samesite_none, "[samesite_none=reporting]", "Credential value was correct");
+ assert_equals(cookies.samesite_unspecified, "[samesite_unspecified=reporting]", "Credential value was correct");
+ assert_equals(cookies.samesite_lax, "[samesite_lax=reporting]", "Credential value was correct");
+ assert_equals(cookies.samesite_strict, "[samesite_strict=reporting]", "Credential value was correct");
+ assert_equals(Object.keys(cookies).length, 4, "No additional cookies were received");
+
+ }, "Reporting endpoints received credentials.");
+ </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/resources/middle-frame.https.sub.html b/third_party/blink/web_tests/external/wpt/reporting/resources/middle-frame.https.sub.html
new file mode 100644
index 0000000..0dd26ec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/resources/middle-frame.https.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Utility page which embeds a reporting page on the chosen host</title>
+</head>
+<body>
+ <script>
+ const searchParams = new URLSearchParams(window.location.search);
+ const host = searchParams.get('host') || "{{hosts[][]}}";
+ const frame = document.createElement('iframe');
+ frame.src=`https://${host}:{{ports[https][0]}}/reporting/resources/same-origin-report.https.sub.html`;
+ document.body.appendChild(frame);
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/reporting/resources/same-origin-report.https.sub.html b/third_party/blink/web_tests/external/wpt/reporting/resources/same-origin-report.https.sub.html
new file mode 100644
index 0000000..326a0fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/resources/same-origin-report.https.sub.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Generates a CSP violation report and sends it to a same-origin endpoint</title>
+</head>
+<body>
+ <script>
+ const img = document.createElement('img');
+ img.src = "/reporting/resources/fail.png";
+ document.body.appendChild(img);
+ // Post back to the main frame that the report should have been queued.
+ top.postMessage("done", "*");
+ </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/resources/same-origin-report.https.sub.html.sub.headers b/third_party/blink/web_tests/external/wpt/reporting/resources/same-origin-report.https.sub.html.sub.headers
new file mode 100644
index 0000000..8244fafb2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/resources/same-origin-report.https.sub.html.sub.headers
@@ -0,0 +1,2 @@
+Reporting-Endpoints: csp-endpoint="/reporting/resources/report.py?reportID=d0d517bf-891b-457a-b970-8b2b2c81a0bf"
+Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'none'; report-to csp-endpoint
diff --git a/third_party/blink/web_tests/external/wpt/reporting/same-origin-cross-site-credentials.https.sub.html b/third_party/blink/web_tests/external/wpt/reporting/same-origin-cross-site-credentials.https.sub.html
new file mode 100644
index 0000000..258ab8e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/same-origin-cross-site-credentials.https.sub.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that credentials are sent properly in a same-origin but not same-site context</title>
+ <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+ <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+ <script src='resources/report-helper.js'></script>
+</head>
+<body>
+ <script>
+ const base_url = `${location.protocol}//${location.host}`;
+ const endpoint = `${base_url}/reporting/resources/report.py`;
+ const id = 'd0d517bf-891b-457a-b970-8b2b2c81a0bf';
+
+ promise_test(async t => {
+ // If this is not run from the expected origin, then the A->B->A frame embedding will not be correct,
+ // and the cookies set in the top-level page will never be returned with the reports.
+ assert_true(location.href.startsWith("https://{{hosts[][]}}:{{ports[https][0]}}/"),
+ "Test running on unexpected origin; subsequent assertions will fail.");
+
+ // Set credentials, and set up test to clear them afterwards.
+ await fetch('/cookies/resources/setSameSite.py?reporting', {mode: 'no-cors', credentials: 'include', cache: 'no-store'});
+ t.add_cleanup(() => fetch("/cookies/resources/dropSameSite.py", {mode: 'no-cors', credentials: 'include', cache: 'no-store'}));
+
+ // Insert a cross-origin frame which will then frame this origin to
+ // trigger a CSP error.
+ const frame = document.createElement('iframe');
+ frame.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/reporting/resources/middle-frame.https.sub.html";
+ document.body.appendChild(frame);
+
+ // Wait for the inner frame to signal that the report has been generated.
+ await new Promise(resolve => {
+ window.addEventListener('message', ev => {
+ if (ev.data === "done")
+ resolve(ev.data);
+ });
+ document.body.appendChild(frame);
+ });
+
+ const reports = await pollReports(endpoint, id);
+ checkReportExists(reports, 'csp-violation', "https://{{hosts[][]}}:{{ports[https][0]}}/reporting/resources/same-origin-report.https.sub.html");
+
+ // Same-site: None cookies should be sent, but not Lax, Strict, or default cookies.
+ const cookies = await pollCookies(endpoint, id);
+ assert_equals(cookies.samesite_none, "[samesite_none=reporting]", "Credential value was correct");
+ assert_false("samesite_strict" in cookies, "Same-site: Strict cookies should not be sent");
+ assert_false("samesite_lax" in cookies, "Same-site: Lax cookies should not be sent");
+ assert_false("samesite_unspecified" in cookies, "Same-site unspecified cookies should not be sent");
+ assert_equals(Object.keys(cookies).length, 1, "No additional cookies were received");
+ }, "Reporting endpoints received credentials.");
+ </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/reporting/same-origin-same-site-credentials.https.sub.html b/third_party/blink/web_tests/external/wpt/reporting/same-origin-same-site-credentials.https.sub.html
new file mode 100644
index 0000000..9b99edb2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/reporting/same-origin-same-site-credentials.https.sub.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that credentials are sent properly in a same-origin and also same-site nested context</title>
+ <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+ <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+ <script src='resources/report-helper.js'></script>
+</head>
+<body>
+ <script>
+ const base_url = `${location.protocol}//${location.host}`;
+ const endpoint = `${base_url}/reporting/resources/report.py`;
+ const id = 'd0d517bf-891b-457a-b970-8b2b2c81a0bf';
+
+ promise_test(async t => {
+ // If this is not run from the expected origin, then the A->A->A frame embedding will not be correct,
+ // and the cookies set in the top-level page will never be returned with the reports.
+ assert_true(location.href.startsWith("https://{{hosts[][]}}:{{ports[https][0]}}/"),
+ "Test running on unexpected origin; subsequent assertions will fail.");
+
+ // Set credentials, and set up test to clear them afterwards.
+ await fetch('/cookies/resources/setSameSite.py?reporting', {mode: 'no-cors', credentials: 'include', cache: 'no-store'});
+ t.add_cleanup(() => fetch("/cookies/resources/dropSameSite.py", {mode: 'no-cors', credentials: 'include', cache: 'no-store'}));
+
+ // Insert a same-origin frame which will then frame this origin to
+ // trigger a CSP error.
+ const frame = document.createElement('iframe');
+ frame.src = "https://{{hosts[][]}}:{{ports[https][0]}}/reporting/resources/middle-frame.https.sub.html";
+ document.body.appendChild(frame);
+
+ // Wait for the inner frame to signal that the report has been generated.
+ await new Promise(resolve => {
+ window.addEventListener('message', ev => {
+ if (ev.data === "done")
+ resolve(ev.data);
+ });
+ document.body.appendChild(frame);
+ });
+
+ const reports = await pollReports(endpoint, id);
+ checkReportExists(reports, 'csp-violation', "https://{{hosts[][]}}:{{ports[https][0]}}/reporting/resources/same-origin-report.https.sub.html");
+
+ // All credentials set at the top-level should be received.
+ const cookies = await pollCookies(endpoint, id);
+ assert_equals(cookies.samesite_none, "[samesite_none=reporting]", "Credential value was correct");
+ assert_equals(cookies.samesite_unspecified, "[samesite_unspecified=reporting]", "Credential value was correct");
+ assert_equals(cookies.samesite_lax, "[samesite_lax=reporting]", "Credential value was correct");
+ assert_equals(cookies.samesite_strict, "[samesite_strict=reporting]", "Credential value was correct");
+ assert_equals(Object.keys(cookies).length, 4, "No additional cookies were received");
+ }, "Reporting endpoints received credentials.");
+ </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/cross-origin-report-no-credentials-report-to.https.sub.html b/third_party/blink/web_tests/wpt_internal/reporting/cross-origin-report-no-credentials-report-to.https.sub.html
new file mode 100644
index 0000000..d4ff60f3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/reporting/cross-origin-report-no-credentials-report-to.https.sub.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that reports are sent without credentials to cross-origin endpoints</title>
+ <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+ <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+ <script src='resources/report-helper.js'></script>
+</head>
+<body>
+ <script>
+ const base_url = `${location.protocol}//${location.host}`;
+ const endpoint = `${base_url}/reporting/resources/report.py`;
+ const id = 'd0d517bf-891b-457a-b970-8b2b2c81a0bf';
+
+ promise_test(async t => {
+ // Set credentials, and set up test to clear them afterwards.
+ await fetch('/cookies/resources/set-cookie.py?name=report&path=%2F', {mode: 'no-cors', credentials: 'include', cache: 'no-store'});
+ t.add_cleanup(() => fetch("/cookies/resources/set.py?report=; path=%2F; expires=Thu, 01 Jan 1970 00:00:01 GMT"));
+
+ // Trigger a CSP error.
+ await new Promise(resolve => {
+ const img = document.createElement('img');
+ img.src = "/reporting/resources/fail.png";
+ img.addEventListener('error', resolve);
+ document.body.appendChild(img);
+ });
+
+ // Wait for report to be received.
+ await wait(3000);
+ const reports = await pollReports(endpoint, id);
+ checkReportExists(reports, 'csp-violation', location.href);
+
+ // Validate that credentials were not sent to cross-origin endpoint.
+ const cookies = await pollCookies(endpoint, id);
+ assert_equals(Object.keys(cookies).length, 0, "Credentials were absent from report");
+ }, "Reporting endpoints did not receive credentials.");
+ </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/cross-origin-report-no-credentials-report-to.https.sub.html.sub.headers b/third_party/blink/web_tests/wpt_internal/reporting/cross-origin-report-no-credentials-report-to.https.sub.html.sub.headers
new file mode 100644
index 0000000..93a84e9
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/reporting/cross-origin-report-no-credentials-report-to.https.sub.html.sub.headers
@@ -0,0 +1,2 @@
+Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://{{domains[www1]}}:{{ports[https][0]}}/reporting/resources/report.py?reportID=d0d517bf-891b-457a-b970-8b2b2c81a0bf","priority":1}]}
+Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'none'; report-to csp-endpoint
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/resources/report-helper.js b/third_party/blink/web_tests/wpt_internal/reporting/resources/report-helper.js
new file mode 100644
index 0000000..cc4f07f0
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/reporting/resources/report-helper.js
@@ -0,0 +1,38 @@
+function wait(ms) {
+ return new Promise(resolve => step_timeout(resolve, ms));
+}
+
+async function pollReports(endpoint, id, min_count) {
+ const res = await fetch(`${endpoint}?reportID=${id}${min_count ? `&min_count=${min_count}` : ''}`, {cache: 'no-store'});
+ const reports = [];
+ if (res.status === 200) {
+ for (const report of await res.json()) {
+ reports.push(report);
+ }
+ }
+ return reports;
+}
+
+async function pollCookies(endpoint, id) {
+ const res = await fetch(`${endpoint}?reportID=${id}&op=retrieve_cookies`, {cache: 'no-store'});
+ const dict = await res.json();
+ if (dict.reportCookies == 'None')
+ return {};
+ return dict.reportCookies;
+}
+
+async function pollNumResults(endpoint, id) {
+ const res = await fetch(`${endpoint}?reportID=${id}&op=retrieve_count`, {cache: 'no-store'});
+ const dict = await res.json();
+ if (dict.report_count == 'None')
+ return 0;
+ return JSON.parse(dict.report_count);
+}
+
+function checkReportExists(reports, type, url) {
+ for (const report of reports) {
+ if (report.type !== type) continue;
+ if (report.body.documentURL == url || report.body.sourceFile === url) return true;
+ }
+ assert_unreached(`A report of ${type} from ${url} is not found.`);
+}
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/resources/report.py b/third_party/blink/web_tests/wpt_internal/reporting/resources/report.py
new file mode 100644
index 0000000..244feb5b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/reporting/resources/report.py
@@ -0,0 +1,147 @@
+import time
+import json
+import re
+import uuid
+
+from wptserve.utils import isomorphic_decode
+
+
+def retrieve_from_stash(request,
+ key,
+ timeout,
+ default_value,
+ min_count=None,
+ retain=False):
+ """Retrieve the set of reports for a given report ID.
+
+ This will extract either the set of reports, credentials, or request count
+ from the stash (depending on the key passed in) and return it encoded as JSON.
+
+ When retrieving reports, this will not return any reports until min_count
+ reports have been received.
+
+ If timeout seconds elapse before the requested data can be found in the stash,
+ or before at least min_count reports are received, default_value will be
+ returned instead."""
+ t0 = time.time()
+ while time.time() - t0 < timeout:
+ time.sleep(0.5)
+ with request.server.stash.lock:
+ value = request.server.stash.take(key=key)
+ if value is not None:
+ have_sufficient_reports = (min_count is None
+ or len(value) >= min_count)
+ if retain or not have_sufficient_reports:
+ request.server.stash.put(key=key, value=value)
+ if have_sufficient_reports:
+ return json.dumps(value)
+
+ return default_value
+
+
+def main(request, response):
+ # Handle CORS preflight requests
+ if request.method == u'OPTIONS':
+ # Always reject preflights for one subdomain
+ if b"www2" in request.headers[b"Origin"]:
+ return (400, [], u"CORS preflight rejected for www2")
+ return [
+ (b"Content-Type", b"text/plain"),
+ (b"Access-Control-Allow-Origin", b"*"),
+ (b"Access-Control-Allow-Methods", b"post"),
+ (b"Access-Control-Allow-Headers", b"Content-Type"),
+ ], u"CORS allowed"
+
+ if b"reportID" in request.GET:
+ key = request.GET.first(b"reportID")
+ elif b"endpoint" in request.GET:
+ key = uuid.uuid5(uuid.NAMESPACE_OID,
+ isomorphic_decode(
+ request.GET[b'endpoint'])).urn.encode('ascii')[9:]
+ else:
+ response.status = 400
+ return "Either reportID or endpoint parameter is required."
+
+ # Cookie and count keys are derived from the report ID.
+ cookie_key = re.sub(b'^....', b'cccc', key)
+ count_key = re.sub(b'^....', b'dddd', key)
+
+ if request.method == u'GET':
+ try:
+ timeout = float(request.GET.first(b"timeout"))
+ except:
+ timeout = 0.5
+ try:
+ min_count = int(request.GET.first(b"min_count"))
+ except:
+ min_count = 1
+ retain = (b"retain" in request.GET)
+
+ op = request.GET.first(b"op", b"")
+ if op in (b"retrieve_report", b""):
+ return [(b"Content-Type", b"application/json")
+ ], retrieve_from_stash(request, key, timeout, u'[]',
+ min_count, retain)
+
+ if op == b"retrieve_cookies":
+ return [(b"Content-Type", b"application/json")
+ ], u"{ \"reportCookies\" : " + str(
+ retrieve_from_stash(request, cookie_key, timeout,
+ u"\"None\"")) + u"}"
+
+ if op == b"retrieve_count":
+ return [(b"Content-Type", b"application/json")], json.dumps({
+ u'report_count':
+ str(retrieve_from_stash(request, count_key, timeout, 0))
+ })
+
+ response.status = 400
+ return "op parameter value not recognized."
+
+ # Save cookies.
+ if len(request.cookies.keys()) > 0:
+ # Convert everything into strings and dump it into a dict.
+ temp_cookies_dict = {}
+ for dict_key in request.cookies.keys():
+ temp_cookies_dict[isomorphic_decode(dict_key)] = str(
+ request.cookies.get_list(dict_key))
+ with request.server.stash.lock:
+ # Clear any existing cookie data for this request before storing new data.
+ request.server.stash.take(key=cookie_key)
+ request.server.stash.put(key=cookie_key, value=temp_cookies_dict)
+
+ # Append new report(s).
+ new_reports = json.loads(request.body)
+
+ # If the incoming report is a CSP report-uri report, then it will be a single
+ # dictionary rather than a list of reports. To handle this case, ensure that
+ # any non-list request bodies are wrapped in a list.
+ if not isinstance(new_reports, list):
+ new_reports = [new_reports]
+
+ for report in new_reports:
+ report[u"metadata"] = {
+ u"content_type":
+ isomorphic_decode(request.headers[b"Content-Type"]),
+ }
+
+ with request.server.stash.lock:
+ reports = request.server.stash.take(key=key)
+ if reports is None:
+ reports = []
+ reports.extend(new_reports)
+ request.server.stash.put(key=key, value=reports)
+
+ # Increment report submission count. This tracks the number of times this
+ # reporting endpoint was contacted, rather than the total number of reports
+ # submitted, which can be seen from the length of the report list.
+ with request.server.stash.lock:
+ count = request.server.stash.take(key=count_key)
+ if count is None:
+ count = 0
+ count += 1
+ request.server.stash.put(key=count_key, value=count)
+
+ # Return acknowledgement report.
+ return [(b"Content-Type", b"text/plain")
+ ], b"Recorded report " + request.body
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/same-origin-report-no-credentials-report-to.https.sub.html b/third_party/blink/web_tests/wpt_internal/reporting/same-origin-report-no-credentials-report-to.https.sub.html
new file mode 100644
index 0000000..158e3f3
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/reporting/same-origin-report-no-credentials-report-to.https.sub.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that reports are sent without credentials to same-origin endpoints when using Report-To</title>
+ <script src='/https/chromium.googlesource.com/resources/testharness.js'></script>
+ <script src='/https/chromium.googlesource.com/resources/testharnessreport.js'></script>
+ <script src='resources/report-helper.js'></script>
+</head>
+<body>
+ <script>
+ const base_url = `${location.protocol}//${location.host}`;
+ const endpoint = `${base_url}/reporting/resources/report.py`;
+ const id = 'd0d517bf-891b-457a-b970-8b2b2c81a0bf';
+
+ promise_test(async t => {
+ // Set credentials, and set up test to clear them afterwards.
+ await fetch('/cookies/resources/set-cookie.py?name=report&path=%2F', {mode: 'no-cors', credentials: 'include', cache: 'no-store'});
+ t.add_cleanup(() => fetch("/cookies/resources/set.py?report=; path=%2F; expires=Thu, 01 Jan 1970 00:00:01 GMT"));
+
+ // Trigger a CSP error.
+ await new Promise(resolve => {
+ const img = document.createElement('img');
+ img.src = "/reporting/resources/fail.png";
+ img.addEventListener('error', resolve);
+ document.body.appendChild(img);
+ });
+
+ // Wait for report to be received.
+ await wait(3000);
+ const reports = await pollReports(endpoint, id);
+ checkReportExists(reports, 'csp-violation', location.href);
+
+ // Validate that credentials were not sent to same-origin endpoint when
+ // configured with Report-To.
+ const cookies = await pollCookies(endpoint, id);
+ assert_equals(Object.keys(cookies).length, 0, "Credentials were absent from report");
+ }, "Reporting endpoints did not receive credentials.");
+ </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/wpt_internal/reporting/same-origin-report-no-credentials-report-to.https.sub.html.sub.headers b/third_party/blink/web_tests/wpt_internal/reporting/same-origin-report-no-credentials-report-to.https.sub.html.sub.headers
new file mode 100644
index 0000000..a8b81c0
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/reporting/same-origin-report-no-credentials-report-to.https.sub.html.sub.headers
@@ -0,0 +1,2 @@
+Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"/reporting/resources/report.py?reportID=d0d517bf-891b-457a-b970-8b2b2c81a0bf","priority":1}]}
+Content-Security-Policy: script-src 'self' 'unsafe-inline'; img-src 'none'; report-to csp-endpoint