1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/reporting/reporting_service.h"
6
7 #include <memory>
8 #include <optional>
9 #include <string>
10
11 #include "base/functional/bind.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/time/tick_clock.h"
16 #include "base/values.h"
17 #include "net/base/features.h"
18 #include "net/base/isolation_info.h"
19 #include "net/base/network_anonymization_key.h"
20 #include "net/base/schemeful_site.h"
21 #include "net/reporting/mock_persistent_reporting_store.h"
22 #include "net/reporting/reporting_browsing_data_remover.h"
23 #include "net/reporting/reporting_cache.h"
24 #include "net/reporting/reporting_context.h"
25 #include "net/reporting/reporting_endpoint.h"
26 #include "net/reporting/reporting_policy.h"
27 #include "net/reporting/reporting_report.h"
28 #include "net/reporting/reporting_service.h"
29 #include "net/reporting/reporting_target_type.h"
30 #include "net/reporting/reporting_test_util.h"
31 #include "net/test/test_with_task_environment.h"
32 #include "net/url_request/url_request_context_builder.h"
33 #include "net/url_request/url_request_test_util.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "url/gurl.h"
37 #include "url/origin.h"
38
39 namespace net {
40 namespace {
41
42 using CommandType = MockPersistentReportingStore::Command::Type;
43
44 // The tests are parametrized on a boolean value which represents whether to use
45 // a MockPersistentReportingStore (if false, no store is used).
46 class ReportingServiceTest : public ::testing::TestWithParam<bool>,
47 public WithTaskEnvironment {
48 protected:
49 const GURL kUrl_ = GURL("https://origin/path");
50 const GURL kUrl2_ = GURL("https://origin2/path");
51 const url::Origin kOrigin_ = url::Origin::Create(kUrl_);
52 const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
53 const GURL kEndpoint_ = GURL("https://endpoint/");
54 const GURL kEndpoint2_ = GURL("https://endpoint2/");
55 const std::string kUserAgent_ = "Mozilla/1.0";
56 const std::string kGroup_ = "group";
57 const std::string kGroup2_ = "group2";
58 const std::string kType_ = "type";
59 const std::optional<base::UnguessableToken> kReportingSource_ =
60 base::UnguessableToken::Create();
61 const NetworkAnonymizationKey kNak_ =
62 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin_));
63 const NetworkAnonymizationKey kNak2_ =
64 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin2_));
65 const ReportingEndpointGroupKey kGroupKey_ =
66 ReportingEndpointGroupKey(kNak_,
67 kOrigin_,
68 kGroup_,
69 ReportingTargetType::kDeveloper);
70 const ReportingEndpointGroupKey kGroupKey2_ =
71 ReportingEndpointGroupKey(kNak2_,
72 kOrigin2_,
73 kGroup_,
74 ReportingTargetType::kDeveloper);
75 const IsolationInfo kIsolationInfo_ =
76 IsolationInfo::Create(IsolationInfo::RequestType::kOther,
77 kOrigin_,
78 kOrigin_,
79 SiteForCookies::FromOrigin(kOrigin_));
80
ReportingServiceTest()81 ReportingServiceTest() {
82 feature_list_.InitAndEnableFeature(
83 features::kPartitionConnectionsByNetworkIsolationKey);
84 Init();
85 }
86
87 // Initializes, or re-initializes, |service_| and its dependencies.
Init()88 void Init() {
89 // Must destroy old service, if there is one, before destroying old store.
90 // Need to clear `context_` first, since it points to an object owned by the
91 // service.
92 context_ = nullptr;
93 service_.reset();
94
95 if (GetParam()) {
96 store_ = std::make_unique<MockPersistentReportingStore>();
97 } else {
98 store_ = nullptr;
99 }
100
101 auto test_context = std::make_unique<TestReportingContext>(
102 &clock_, &tick_clock_, ReportingPolicy(), store_.get());
103 context_ = test_context.get();
104
105 service_ = ReportingService::CreateForTesting(std::move(test_context));
106 }
107
108 // If the store exists, simulate finishing loading the store, which should
109 // make the rest of the test run synchronously.
FinishLoading(bool load_success)110 void FinishLoading(bool load_success) {
111 if (store_) {
112 store_->FinishLoading(load_success);
113 }
114 }
115
store()116 MockPersistentReportingStore* store() { return store_.get(); }
context()117 TestReportingContext* context() { return context_; }
service()118 ReportingService* service() { return service_.get(); }
119
120 private:
121 base::test::ScopedFeatureList feature_list_;
122
123 base::SimpleTestClock clock_;
124 base::SimpleTestTickClock tick_clock_;
125
126 std::unique_ptr<MockPersistentReportingStore> store_;
127 std::unique_ptr<ReportingService> service_;
128 raw_ptr<TestReportingContext> context_ = nullptr;
129 };
130
TEST_P(ReportingServiceTest,QueueReport)131 TEST_P(ReportingServiceTest, QueueReport) {
132 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
133 kType_, base::Value::Dict(), 0,
134 ReportingTargetType::kDeveloper);
135 FinishLoading(true /* load_success */);
136
137 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
138 context()->cache()->GetReports(&reports);
139 ASSERT_EQ(1u, reports.size());
140 EXPECT_EQ(kUrl_, reports[0]->url);
141 EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
142 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
143 EXPECT_EQ(kGroup_, reports[0]->group);
144 EXPECT_EQ(kType_, reports[0]->type);
145 EXPECT_EQ(ReportingTargetType::kDeveloper, reports[0]->target_type);
146 }
147
TEST_P(ReportingServiceTest,QueueEnterpriseReport)148 TEST_P(ReportingServiceTest, QueueEnterpriseReport) {
149 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
150 kType_, base::Value::Dict(), 0,
151 ReportingTargetType::kEnterprise);
152 FinishLoading(true /* load_success */);
153
154 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
155 context()->cache()->GetReports(&reports);
156 ASSERT_EQ(1u, reports.size());
157 EXPECT_EQ(kUrl_, reports[0]->url);
158 EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
159 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
160 EXPECT_EQ(kGroup_, reports[0]->group);
161 EXPECT_EQ(kType_, reports[0]->type);
162 EXPECT_EQ(ReportingTargetType::kEnterprise, reports[0]->target_type);
163 }
164
TEST_P(ReportingServiceTest,QueueReportSanitizeUrl)165 TEST_P(ReportingServiceTest, QueueReportSanitizeUrl) {
166 // Same as kUrl_ but with username, password, and fragment.
167 GURL url = GURL("https://username:password@origin/path#fragment");
168 service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_,
169 kType_, base::Value::Dict(), 0,
170 ReportingTargetType::kDeveloper);
171 FinishLoading(true /* load_success */);
172
173 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
174 context()->cache()->GetReports(&reports);
175 ASSERT_EQ(1u, reports.size());
176 EXPECT_EQ(kUrl_, reports[0]->url);
177 EXPECT_EQ(kNak_, reports[0]->network_anonymization_key);
178 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
179 EXPECT_EQ(kGroup_, reports[0]->group);
180 EXPECT_EQ(kType_, reports[0]->type);
181 }
182
TEST_P(ReportingServiceTest,DontQueueReportInvalidUrl)183 TEST_P(ReportingServiceTest, DontQueueReportInvalidUrl) {
184 GURL url = GURL("https://");
185 // This does not trigger an attempt to load from the store because the url
186 // is immediately rejected as invalid.
187 service()->QueueReport(url, kReportingSource_, kNak_, kUserAgent_, kGroup_,
188 kType_, base::Value::Dict(), 0,
189 ReportingTargetType::kDeveloper);
190
191 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
192 context()->cache()->GetReports(&reports);
193 ASSERT_EQ(0u, reports.size());
194 }
195
TEST_P(ReportingServiceTest,QueueReportNetworkIsolationKeyDisabled)196 TEST_P(ReportingServiceTest, QueueReportNetworkIsolationKeyDisabled) {
197 base::test::ScopedFeatureList feature_list;
198 feature_list.InitAndDisableFeature(
199 features::kPartitionConnectionsByNetworkIsolationKey);
200
201 // Re-create the store, so it reads the new feature value.
202 Init();
203
204 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
205 kType_, base::Value::Dict(), 0,
206 ReportingTargetType::kDeveloper);
207 FinishLoading(true /* load_success */);
208
209 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
210 context()->cache()->GetReports(&reports);
211 ASSERT_EQ(1u, reports.size());
212
213 // NetworkAnonymizationKey should be empty, instead of kNak_;
214 EXPECT_EQ(NetworkAnonymizationKey(), reports[0]->network_anonymization_key);
215 EXPECT_NE(kNak_, reports[0]->network_anonymization_key);
216
217 EXPECT_EQ(kUrl_, reports[0]->url);
218 EXPECT_EQ(kUserAgent_, reports[0]->user_agent);
219 EXPECT_EQ(kGroup_, reports[0]->group);
220 EXPECT_EQ(kType_, reports[0]->type);
221 }
222
TEST_P(ReportingServiceTest,ProcessReportToHeader)223 TEST_P(ReportingServiceTest, ProcessReportToHeader) {
224 service()->ProcessReportToHeader(kOrigin_, kNak_,
225 "{\"endpoints\":[{\"url\":\"" +
226 kEndpoint_.spec() +
227 "\"}],"
228 "\"group\":\"" +
229 kGroup_ +
230 "\","
231 "\"max_age\":86400}");
232 FinishLoading(true /* load_success */);
233
234 EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
235 EXPECT_TRUE(context()->cache()->GetEndpointForTesting(
236 ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_,
237 ReportingTargetType::kDeveloper),
238 kEndpoint_));
239 }
240
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeader)241 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeader) {
242 base::test::ScopedFeatureList feature_list;
243 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
244 auto parsed_header =
245 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
246 ASSERT_TRUE(parsed_header.has_value());
247 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
248 kIsolationInfo_, *parsed_header);
249 FinishLoading(true /* load_success */);
250
251 // Endpoint should not be part of the persistent store.
252 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
253 // Endpoint should be associated with the reporting source.
254 ReportingEndpoint cached_endpoint =
255 context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
256 EXPECT_TRUE(cached_endpoint);
257
258 // Ensure that the NAK is stored properly with the endpoint group.
259 EXPECT_FALSE(cached_endpoint.group_key.network_anonymization_key.IsEmpty());
260 }
261
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled)262 TEST_P(ReportingServiceTest,
263 ProcessReportingEndpointsHeaderNetworkIsolationKeyDisabled) {
264 base::test::ScopedFeatureList feature_list;
265 feature_list.InitWithFeatures(
266 {net::features::kDocumentReporting},
267 {features::kPartitionConnectionsByNetworkIsolationKey});
268
269 // Re-create the store, so it reads the new feature value.
270 Init();
271
272 auto parsed_header =
273 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\"");
274 ASSERT_TRUE(parsed_header.has_value());
275 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
276 kIsolationInfo_, *parsed_header);
277 FinishLoading(true /* load_success */);
278
279 // Endpoint should not be part of the persistent store.
280 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
281 // Endpoint should be associated with the reporting source.
282 ReportingEndpoint cached_endpoint =
283 context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
284 EXPECT_TRUE(cached_endpoint);
285
286 // When isolation is disabled, cached endpoints should have a null NAK.
287 EXPECT_TRUE(cached_endpoint.group_key.network_anonymization_key.IsEmpty());
288 }
289
TEST_P(ReportingServiceTest,SendReportsAndRemoveSource)290 TEST_P(ReportingServiceTest, SendReportsAndRemoveSource) {
291 base::test::ScopedFeatureList feature_list;
292 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
293 auto parsed_header =
294 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
295 kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
296 ASSERT_TRUE(parsed_header.has_value());
297 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
298 kIsolationInfo_, *parsed_header);
299 // This report should be sent immediately, starting the delivery agent timer.
300 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
301 kType_, base::Value::Dict(), 0,
302 ReportingTargetType::kDeveloper);
303
304 FinishLoading(true /* load_success */);
305
306 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
307 context()->cache()->GetReports(&reports);
308 ASSERT_EQ(1u, reports.size());
309 EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
310 ReportingReport::Status::QUEUED));
311
312 // Now simulate the source being destroyed.
313 service()->SendReportsAndRemoveSource(*kReportingSource_);
314
315 // There should be no queued reports, but the previously sent report should
316 // still be pending.
317 EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
318 ReportingReport::Status::QUEUED));
319 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
320 ReportingReport::Status::PENDING));
321 // Source should be marked as expired.
322 ASSERT_TRUE(
323 context()->cache()->GetExpiredSources().contains(*kReportingSource_));
324 }
325
326 // Flaky in ChromeOS: crbug.com/1356127
327 #if BUILDFLAG(IS_CHROMEOS)
328 #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \
329 DISABLED_SendReportsAndRemoveSourceWithPendingReports
330 #else
331 #define MAYBE_SendReportsAndRemoveSourceWithPendingReports \
332 SendReportsAndRemoveSourceWithPendingReports
333 #endif
TEST_P(ReportingServiceTest,MAYBE_SendReportsAndRemoveSourceWithPendingReports)334 TEST_P(ReportingServiceTest,
335 MAYBE_SendReportsAndRemoveSourceWithPendingReports) {
336 base::test::ScopedFeatureList feature_list;
337 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
338 auto parsed_header =
339 ParseReportingEndpoints(kGroup_ + "=\"" + kEndpoint_.spec() + "\", " +
340 kGroup2_ + "=\"" + kEndpoint2_.spec() + "\"");
341 ASSERT_TRUE(parsed_header.has_value());
342 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
343 kIsolationInfo_, *parsed_header);
344 // This report should be sent immediately, starting the delivery agent timer.
345 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
346 kType_, base::Value::Dict(), 0,
347 ReportingTargetType::kDeveloper);
348
349 FinishLoading(true /* load_success */);
350
351 std::vector<raw_ptr<const ReportingReport, VectorExperimental>> reports;
352 context()->cache()->GetReports(&reports);
353 ASSERT_EQ(1u, reports.size());
354 EXPECT_EQ(0u, context()->cache()->GetReportCountWithStatusForTesting(
355 ReportingReport::Status::QUEUED));
356 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
357 ReportingReport::Status::PENDING));
358
359 // Queue another report, which should remain queued.
360 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
361 kType_, base::Value::Dict(), 0,
362 ReportingTargetType::kDeveloper);
363 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
364 ReportingReport::Status::QUEUED));
365 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
366 ReportingReport::Status::PENDING));
367
368 // Now simulate the source being destroyed.
369 service()->SendReportsAndRemoveSource(*kReportingSource_);
370
371 // The report should still be queued, while the source should be marked as
372 // expired. (The original report is still pending.)
373 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
374 ReportingReport::Status::QUEUED));
375 EXPECT_EQ(1u, context()->cache()->GetReportCountWithStatusForTesting(
376 ReportingReport::Status::PENDING));
377 ASSERT_TRUE(
378 context()->cache()->GetExpiredSources().contains(kReportingSource_));
379 }
380
TEST_P(ReportingServiceTest,ProcessReportingEndpointsHeaderPathAbsolute)381 TEST_P(ReportingServiceTest, ProcessReportingEndpointsHeaderPathAbsolute) {
382 base::test::ScopedFeatureList feature_list;
383 feature_list.InitAndEnableFeature(net::features::kDocumentReporting);
384 auto parsed_header = ParseReportingEndpoints(kGroup_ + "=\"/path-absolute\"");
385 ASSERT_TRUE(parsed_header.has_value());
386 service()->SetDocumentReportingEndpoints(*kReportingSource_, kOrigin_,
387 kIsolationInfo_, *parsed_header);
388 FinishLoading(true /* load_success */);
389
390 // Endpoint should not be part of the persistent store.
391 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
392 // Endpoint should be associated with the reporting source.
393 ReportingEndpoint endpoint =
394 context()->cache()->GetV1EndpointForTesting(*kReportingSource_, kGroup_);
395 EXPECT_TRUE(endpoint);
396 // Endpoint should have the correct path.
397 EXPECT_EQ(kUrl_.Resolve("/path-absolute"), endpoint.info.url);
398 }
399
TEST_P(ReportingServiceTest,ProcessReportToHeaderPathAbsolute)400 TEST_P(ReportingServiceTest, ProcessReportToHeaderPathAbsolute) {
401 service()->ProcessReportToHeader(
402 kOrigin_, kNak_,
403 "{\"endpoints\":[{\"url\":\"/path-absolute\"}],"
404 "\"group\":\"" +
405 kGroup_ +
406 "\","
407 "\"max_age\":86400}");
408 FinishLoading(true /* load_success */);
409
410 EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
411 }
412
TEST_P(ReportingServiceTest,ProcessReportToHeader_TooLong)413 TEST_P(ReportingServiceTest, ProcessReportToHeader_TooLong) {
414 const std::string header_too_long =
415 "{\"endpoints\":[{\"url\":\"" + kEndpoint_.spec() +
416 "\"}],"
417 "\"group\":\"" +
418 kGroup_ +
419 "\","
420 "\"max_age\":86400," +
421 "\"junk\":\"" + std::string(32 * 1024, 'a') + "\"}";
422 // This does not trigger an attempt to load from the store because the header
423 // is immediately rejected as invalid.
424 service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_long);
425
426 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
427 }
428
TEST_P(ReportingServiceTest,ProcessReportToHeader_TooDeep)429 TEST_P(ReportingServiceTest, ProcessReportToHeader_TooDeep) {
430 const std::string header_too_deep = "{\"endpoints\":[{\"url\":\"" +
431 kEndpoint_.spec() +
432 "\"}],"
433 "\"group\":\"" +
434 kGroup_ +
435 "\","
436 "\"max_age\":86400," +
437 "\"junk\":[[[[[[[[[[]]]]]]]]]]}";
438 // This does not trigger an attempt to load from the store because the header
439 // is immediately rejected as invalid.
440 service()->ProcessReportToHeader(kOrigin_, kNak_, header_too_deep);
441
442 EXPECT_EQ(0u, context()->cache()->GetEndpointCount());
443 }
444
TEST_P(ReportingServiceTest,ProcessReportToHeaderNetworkIsolationKeyDisabled)445 TEST_P(ReportingServiceTest, ProcessReportToHeaderNetworkIsolationKeyDisabled) {
446 base::test::ScopedFeatureList feature_list;
447 feature_list.InitAndDisableFeature(
448 features::kPartitionConnectionsByNetworkIsolationKey);
449
450 // Re-create the store, so it reads the new feature value.
451 Init();
452
453 service()->ProcessReportToHeader(kOrigin_, kNak_,
454 "{\"endpoints\":[{\"url\":\"" +
455 kEndpoint_.spec() +
456 "\"}],"
457 "\"group\":\"" +
458 kGroup_ +
459 "\","
460 "\"max_age\":86400}");
461 FinishLoading(true /* load_success */);
462
463 EXPECT_EQ(1u, context()->cache()->GetEndpointCount());
464 EXPECT_FALSE(context()->cache()->GetEndpointForTesting(
465 ReportingEndpointGroupKey(kNak_, kOrigin_, kGroup_,
466 ReportingTargetType::kDeveloper),
467 kEndpoint_));
468 EXPECT_TRUE(context()->cache()->GetEndpointForTesting(
469 ReportingEndpointGroupKey(NetworkAnonymizationKey(), kOrigin_, kGroup_,
470 ReportingTargetType::kDeveloper),
471 kEndpoint_));
472 }
473
TEST_P(ReportingServiceTest,WriteToStore)474 TEST_P(ReportingServiceTest, WriteToStore) {
475 if (!store()) {
476 return;
477 }
478
479 MockPersistentReportingStore::CommandList expected_commands;
480
481 // This first call to any public method triggers a load. The load will block
482 // until we call FinishLoading.
483 service()->ProcessReportToHeader(kOrigin_, kNak_,
484 "{\"endpoints\":[{\"url\":\"" +
485 kEndpoint_.spec() +
486 "\"}],"
487 "\"group\":\"" +
488 kGroup_ +
489 "\","
490 "\"max_age\":86400}");
491 expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
492 EXPECT_THAT(store()->GetAllCommands(),
493 testing::UnorderedElementsAreArray(expected_commands));
494
495 // Unblock the load. The will let the remaining calls to the service complete
496 // without blocking.
497 FinishLoading(true /* load_success */);
498 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
499 kGroupKey_, kEndpoint_);
500 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
501 kGroupKey_);
502 EXPECT_THAT(store()->GetAllCommands(),
503 testing::UnorderedElementsAreArray(expected_commands));
504
505 service()->ProcessReportToHeader(kOrigin2_, kNak2_,
506 "{\"endpoints\":[{\"url\":\"" +
507 kEndpoint_.spec() +
508 "\"}],"
509 "\"group\":\"" +
510 kGroup_ +
511 "\","
512 "\"max_age\":86400}");
513 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
514 kGroupKey2_, kEndpoint_);
515 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
516 kGroupKey2_);
517 EXPECT_THAT(store()->GetAllCommands(),
518 testing::UnorderedElementsAreArray(expected_commands));
519
520 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
521 kType_, base::Value::Dict(), 0,
522 ReportingTargetType::kDeveloper);
523 expected_commands.emplace_back(
524 CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_);
525 EXPECT_THAT(store()->GetAllCommands(),
526 testing::UnorderedElementsAreArray(expected_commands));
527
528 service()->RemoveBrowsingData(
529 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
530 base::BindRepeating(
531 [](const url::Origin& origin) { return origin.host() == "origin"; }));
532 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
533 kGroupKey_, kEndpoint_);
534 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
535 kGroupKey_);
536 expected_commands.emplace_back(CommandType::FLUSH);
537 EXPECT_THAT(store()->GetAllCommands(),
538 testing::UnorderedElementsAreArray(expected_commands));
539
540 service()->RemoveAllBrowsingData(
541 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
542 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
543 kGroupKey2_, kEndpoint_);
544 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
545 kGroupKey2_);
546 expected_commands.emplace_back(CommandType::FLUSH);
547 EXPECT_THAT(store()->GetAllCommands(),
548 testing::UnorderedElementsAreArray(expected_commands));
549 }
550
TEST_P(ReportingServiceTest,WaitUntilLoadFinishesBeforeWritingToStore)551 TEST_P(ReportingServiceTest, WaitUntilLoadFinishesBeforeWritingToStore) {
552 if (!store()) {
553 return;
554 }
555
556 MockPersistentReportingStore::CommandList expected_commands;
557
558 // This first call to any public method triggers a load. The load will block
559 // until we call FinishLoading.
560 service()->ProcessReportToHeader(kOrigin_, kNak_,
561 "{\"endpoints\":[{\"url\":\"" +
562 kEndpoint_.spec() +
563 "\"}],"
564 "\"group\":\"" +
565 kGroup_ +
566 "\","
567 "\"max_age\":86400}");
568 expected_commands.emplace_back(CommandType::LOAD_REPORTING_CLIENTS);
569 EXPECT_THAT(store()->GetAllCommands(),
570 testing::UnorderedElementsAreArray(expected_commands));
571
572 service()->ProcessReportToHeader(kOrigin2_, kNak2_,
573 "{\"endpoints\":[{\"url\":\"" +
574 kEndpoint_.spec() +
575 "\"}],"
576 "\"group\":\"" +
577 kGroup_ +
578 "\","
579 "\"max_age\":86400}");
580 EXPECT_THAT(store()->GetAllCommands(),
581 testing::UnorderedElementsAreArray(expected_commands));
582
583 service()->QueueReport(kUrl_, kReportingSource_, kNak_, kUserAgent_, kGroup_,
584 kType_, base::Value::Dict(), 0,
585 ReportingTargetType::kDeveloper);
586 EXPECT_THAT(store()->GetAllCommands(),
587 testing::UnorderedElementsAreArray(expected_commands));
588
589 service()->RemoveBrowsingData(
590 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS,
591 base::BindRepeating(
592 [](const url::Origin& origin) { return origin.host() == "origin"; }));
593 EXPECT_THAT(store()->GetAllCommands(),
594 testing::UnorderedElementsAreArray(expected_commands));
595
596 service()->RemoveAllBrowsingData(
597 ReportingBrowsingDataRemover::DATA_TYPE_CLIENTS);
598 EXPECT_THAT(store()->GetAllCommands(),
599 testing::UnorderedElementsAreArray(expected_commands));
600
601 // Unblock the load. The will let the remaining calls to the service complete
602 // without blocking.
603 FinishLoading(true /* load_success */);
604 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
605 kGroupKey_, kEndpoint_);
606 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
607 kGroupKey2_, kEndpoint_);
608 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
609 kGroupKey_);
610 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
611 kGroupKey2_);
612 expected_commands.emplace_back(
613 CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_ACCESS_TIME, kGroupKey_);
614 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
615 kGroupKey_, kEndpoint_);
616 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
617 kGroupKey_);
618 expected_commands.emplace_back(CommandType::FLUSH);
619 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
620 kGroupKey2_, kEndpoint_);
621 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
622 kGroupKey2_);
623 expected_commands.emplace_back(CommandType::FLUSH);
624 EXPECT_THAT(store()->GetAllCommands(),
625 testing::UnorderedElementsAreArray(expected_commands));
626 }
627
TEST_P(ReportingServiceTest,SetEnterpriseReportingEndpointsWithFeatureEnabled)628 TEST_P(ReportingServiceTest,
629 SetEnterpriseReportingEndpointsWithFeatureEnabled) {
630 base::test::ScopedFeatureList feature_list;
631 feature_list.InitAndEnableFeature(
632 net::features::kReportingApiEnableEnterpriseCookieIssues);
633 EXPECT_EQ(0u, context()->cache()->GetEnterpriseEndpointsForTesting().size());
634 base::flat_map<std::string, GURL> test_enterprise_endpoints{
635 {"endpoint-1", GURL("https://example.com/reports")},
636 {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
637 {"endpoint-3", GURL("https://report-collector.example")},
638 };
639
640 std::vector<ReportingEndpoint> expected_enterprise_endpoints = {
641 {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
642 /*reporting_source=*/std::nullopt,
643 /*origin=*/std::nullopt, "endpoint-1",
644 ReportingTargetType::kEnterprise),
645 {.url = GURL("https://example.com/reports")}},
646 {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
647 /*reporting_source=*/std::nullopt,
648 /*origin=*/std::nullopt, "endpoint-2",
649 ReportingTargetType::kEnterprise),
650 {.url = GURL("https://reporting.example/cookie-issues")}},
651 {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
652 /*reporting_source=*/std::nullopt,
653 /*origin=*/std::nullopt, "endpoint-3",
654 ReportingTargetType::kEnterprise),
655 {.url = GURL("https://report-collector.example")}}};
656
657 service()->SetEnterpriseReportingEndpoints(test_enterprise_endpoints);
658 EXPECT_EQ(expected_enterprise_endpoints,
659 context()->cache()->GetEnterpriseEndpointsForTesting());
660 }
661
TEST_P(ReportingServiceTest,SetEnterpriseReportingEndpointsWithFeatureDisabled)662 TEST_P(ReportingServiceTest,
663 SetEnterpriseReportingEndpointsWithFeatureDisabled) {
664 EXPECT_EQ(0u, context()->cache()->GetEnterpriseEndpointsForTesting().size());
665 base::flat_map<std::string, GURL> test_enterprise_endpoints{
666 {"endpoint-1", GURL("https://example.com/reports")},
667 {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
668 {"endpoint-3", GURL("https://report-collector.example")},
669 };
670
671 service()->SetEnterpriseReportingEndpoints(test_enterprise_endpoints);
672 EXPECT_EQ(0u, context()->cache()->GetEnterpriseEndpointsForTesting().size());
673 }
674
TEST_P(ReportingServiceTest,ReportingServiceConstructionWithFeatureEnabled)675 TEST_P(ReportingServiceTest, ReportingServiceConstructionWithFeatureEnabled) {
676 base::test::ScopedFeatureList feature_list;
677 feature_list.InitAndEnableFeature(
678 net::features::kReportingApiEnableEnterpriseCookieIssues);
679 base::flat_map<std::string, GURL> test_enterprise_endpoints{
680 {"endpoint-1", GURL("https://example.com/reports")},
681 {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
682 {"endpoint-3", GURL("https://report-collector.example")},
683 };
684
685 EXPECT_EQ(0u, service()
686 ->GetContextForTesting()
687 ->cache()
688 ->GetEnterpriseEndpointsForTesting()
689 .size());
690 std::unique_ptr<URLRequestContext> url_request_context =
691 CreateTestURLRequestContextBuilder()->Build();
692 std::unique_ptr<ReportingService> reporting_service_ptr =
693 ReportingService::Create(ReportingPolicy(), url_request_context.get(),
694 store(), test_enterprise_endpoints);
695
696 std::vector<ReportingEndpoint> expected_enterprise_endpoints = {
697 {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
698 /*reporting_source=*/std::nullopt,
699 /*origin=*/std::nullopt, "endpoint-1",
700 ReportingTargetType::kEnterprise),
701 {.url = GURL("https://example.com/reports")}},
702 {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
703 /*reporting_source=*/std::nullopt,
704 /*origin=*/std::nullopt, "endpoint-2",
705 ReportingTargetType::kEnterprise),
706 {.url = GURL("https://reporting.example/cookie-issues")}},
707 {ReportingEndpointGroupKey(NetworkAnonymizationKey(),
708 /*reporting_source=*/std::nullopt,
709 /*origin=*/std::nullopt, "endpoint-3",
710 ReportingTargetType::kEnterprise),
711 {.url = GURL("https://report-collector.example")}}};
712
713 EXPECT_EQ(expected_enterprise_endpoints,
714 reporting_service_ptr->GetContextForTesting()
715 ->cache()
716 ->GetEnterpriseEndpointsForTesting());
717 }
718
TEST_P(ReportingServiceTest,ReportingServiceConstructionWithFeatureDisabled)719 TEST_P(ReportingServiceTest, ReportingServiceConstructionWithFeatureDisabled) {
720 base::test::ScopedFeatureList feature_list;
721 feature_list.InitAndDisableFeature(
722 net::features::kReportingApiEnableEnterpriseCookieIssues);
723 base::flat_map<std::string, GURL> test_enterprise_endpoints{
724 {"endpoint-1", GURL("https://example.com/reports")},
725 {"endpoint-2", GURL("https://reporting.example/cookie-issues")},
726 {"endpoint-3", GURL("https://report-collector.example")},
727 };
728
729 EXPECT_EQ(0u, service()
730 ->GetContextForTesting()
731 ->cache()
732 ->GetEnterpriseEndpointsForTesting()
733 .size());
734 std::unique_ptr<URLRequestContext> url_request_context =
735 CreateTestURLRequestContextBuilder()->Build();
736 std::unique_ptr<ReportingService> reporting_service_ptr =
737 ReportingService::Create(ReportingPolicy(), url_request_context.get(),
738 store(), test_enterprise_endpoints);
739
740 EXPECT_EQ(0u, reporting_service_ptr->GetContextForTesting()
741 ->cache()
742 ->GetEnterpriseEndpointsForTesting()
743 .size());
744 }
745
746 INSTANTIATE_TEST_SUITE_P(ReportingServiceStoreTest,
747 ReportingServiceTest,
748 ::testing::Bool());
749 } // namespace
750 } // namespace net
751