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_header_parser.h"
6
7 #include <optional>
8 #include <sstream>
9 #include <string>
10 #include <vector>
11
12 #include "base/functional/bind.h"
13 #include "base/json/json_reader.h"
14 #include "base/strings/strcat.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/test/metrics/histogram_tester.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/test/simple_test_tick_clock.h"
19 #include "base/time/time.h"
20 #include "base/values.h"
21 #include "net/base/features.h"
22 #include "net/base/isolation_info.h"
23 #include "net/base/network_anonymization_key.h"
24 #include "net/base/schemeful_site.h"
25 #include "net/reporting/mock_persistent_reporting_store.h"
26 #include "net/reporting/reporting_cache.h"
27 #include "net/reporting/reporting_endpoint.h"
28 #include "net/reporting/reporting_test_util.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "url/gurl.h"
31 #include "url/origin.h"
32
33 namespace net {
34 namespace {
35
36 using CommandType = MockPersistentReportingStore::Command::Type;
37 using Dictionary = structured_headers::Dictionary;
38
39 constexpr char kReportingHeaderTypeHistogram[] = "Net.Reporting.HeaderType";
40
41 class ReportingHeaderParserTestBase
42 : public ReportingTestBase,
43 public ::testing::WithParamInterface<bool> {
44 protected:
ReportingHeaderParserTestBase()45 ReportingHeaderParserTestBase() {
46 ReportingPolicy policy;
47 policy.max_endpoints_per_origin = 10;
48 policy.max_endpoint_count = 20;
49 UsePolicy(policy);
50
51 if (GetParam())
52 store_ = std::make_unique<MockPersistentReportingStore>();
53 else
54 store_ = nullptr;
55 UseStore(store_.get());
56 }
57 ~ReportingHeaderParserTestBase() override = default;
58
SetUp()59 void SetUp() override {
60 // All ReportingCache methods assume that the store has been initialized.
61 if (mock_store()) {
62 mock_store()->LoadReportingClients(
63 base::BindOnce(&ReportingCache::AddClientsLoadedFromStore,
64 base::Unretained(cache())));
65 mock_store()->FinishLoading(true);
66 }
67 }
68
mock_store()69 MockPersistentReportingStore* mock_store() { return store_.get(); }
70
71 base::test::ScopedFeatureList feature_list_;
72 const GURL kUrl1_ = GURL("https://origin1.test/path");
73 const url::Origin kOrigin1_ = url::Origin::Create(kUrl1_);
74 const GURL kUrl2_ = GURL("https://origin2.test/path");
75 const url::Origin kOrigin2_ = url::Origin::Create(kUrl2_);
76 const NetworkAnonymizationKey kNak_ =
77 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin1_));
78 const NetworkAnonymizationKey kOtherNak_ =
79 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(kOrigin2_));
80 const IsolationInfo kIsolationInfo_ =
81 IsolationInfo::Create(IsolationInfo::RequestType::kOther,
82 kOrigin1_,
83 kOrigin1_,
84 SiteForCookies::FromOrigin(kOrigin1_));
85 const GURL kUrlEtld_ = GURL("https://co.uk/foo.html/");
86 const url::Origin kOriginEtld_ = url::Origin::Create(kUrlEtld_);
87 const GURL kEndpoint1_ = GURL("https://endpoint1.test/");
88 const GURL kEndpoint2_ = GURL("https://endpoint2.test/");
89 const GURL kEndpoint3_ = GURL("https://endpoint3.test/");
90 const GURL kEndpointPathAbsolute_ =
91 GURL("https://origin1.test/path-absolute-url");
92 const std::string kGroup1_ = "group1";
93 const std::string kGroup2_ = "group2";
94 // There are 2^3 = 8 of these to test the different combinations of matching
95 // vs mismatching NAK, origin, and group.
96 const ReportingEndpointGroupKey kGroupKey11_ =
97 ReportingEndpointGroupKey(kNak_, kOrigin1_, kGroup1_);
98 const ReportingEndpointGroupKey kGroupKey21_ =
99 ReportingEndpointGroupKey(kNak_, kOrigin2_, kGroup1_);
100 const ReportingEndpointGroupKey kGroupKey12_ =
101 ReportingEndpointGroupKey(kNak_, kOrigin1_, kGroup2_);
102 const ReportingEndpointGroupKey kGroupKey22_ =
103 ReportingEndpointGroupKey(kNak_, kOrigin2_, kGroup2_);
104
105 private:
106 std::unique_ptr<MockPersistentReportingStore> store_;
107 };
108
109 // This test is parametrized on a boolean that represents whether to use a
110 // MockPersistentReportingStore.
111 class ReportingHeaderParserTest : public ReportingHeaderParserTestBase {
112 protected:
ReportingHeaderParserTest()113 ReportingHeaderParserTest() {
114 // This is a private API of the reporting service, so no need to test the
115 // case kPartitionNelAndReportingByNetworkIsolationKey is disabled - the
116 // feature is only applied at the entry points of the service.
117 feature_list_.InitAndEnableFeature(
118 features::kPartitionNelAndReportingByNetworkIsolationKey);
119 }
120
MakeEndpointGroup(const std::string & name,const std::vector<ReportingEndpoint::EndpointInfo> & endpoints,OriginSubdomains include_subdomains=OriginSubdomains::DEFAULT,base::TimeDelta ttl=base::Days (1),url::Origin origin=url::Origin ())121 ReportingEndpointGroup MakeEndpointGroup(
122 const std::string& name,
123 const std::vector<ReportingEndpoint::EndpointInfo>& endpoints,
124 OriginSubdomains include_subdomains = OriginSubdomains::DEFAULT,
125 base::TimeDelta ttl = base::Days(1),
126 url::Origin origin = url::Origin()) {
127 ReportingEndpointGroupKey group_key(kNak_ /* unused */,
128 url::Origin() /* unused */, name);
129 ReportingEndpointGroup group;
130 group.group_key = group_key;
131 group.include_subdomains = include_subdomains;
132 group.ttl = ttl;
133 group.endpoints = std::move(endpoints);
134 return group;
135 }
136
137 // Constructs a string which would represent a single group in a Report-To
138 // header. If |group_name| is an empty string, the group name will be omitted
139 // (and thus default to "default" when parsed). Setting |omit_defaults| omits
140 // the priority, weight, and include_subdomains fields if they are default,
141 // otherwise they are spelled out fully.
ConstructHeaderGroupString(const ReportingEndpointGroup & group,bool omit_defaults=true)142 std::string ConstructHeaderGroupString(const ReportingEndpointGroup& group,
143 bool omit_defaults = true) {
144 std::ostringstream s;
145 s << "{ ";
146
147 if (!group.group_key.group_name.empty()) {
148 s << "\"group\": \"" << group.group_key.group_name << "\", ";
149 }
150
151 s << "\"max_age\": " << group.ttl.InSeconds() << ", ";
152
153 if (group.include_subdomains != OriginSubdomains::DEFAULT) {
154 s << "\"include_subdomains\": true, ";
155 } else if (!omit_defaults) {
156 s << "\"include_subdomains\": false, ";
157 }
158
159 s << "\"endpoints\": [";
160 for (const ReportingEndpoint::EndpointInfo& endpoint_info :
161 group.endpoints) {
162 s << "{ ";
163 s << "\"url\": \"" << endpoint_info.url.spec() << "\"";
164
165 if (!omit_defaults ||
166 endpoint_info.priority !=
167 ReportingEndpoint::EndpointInfo::kDefaultPriority) {
168 s << ", \"priority\": " << endpoint_info.priority;
169 }
170
171 if (!omit_defaults ||
172 endpoint_info.weight !=
173 ReportingEndpoint::EndpointInfo::kDefaultWeight) {
174 s << ", \"weight\": " << endpoint_info.weight;
175 }
176
177 s << " }, ";
178 }
179 if (!group.endpoints.empty())
180 s.seekp(-2, s.cur); // Overwrite trailing comma and space.
181 s << "]";
182
183 s << " }";
184
185 return s.str();
186 }
187
ParseHeader(const NetworkAnonymizationKey & network_anonymization_key,const url::Origin & origin,const std::string & json)188 void ParseHeader(const NetworkAnonymizationKey& network_anonymization_key,
189 const url::Origin& origin,
190 const std::string& json) {
191 std::optional<base::Value> value = base::JSONReader::Read("[" + json + "]");
192 if (value) {
193 ReportingHeaderParser::ParseReportToHeader(
194 context(), network_anonymization_key, origin, value->GetList());
195 }
196 }
197 };
198
199 // TODO(juliatuttle): Ideally these tests should be expecting that JSON parsing
200 // (and therefore header parsing) may happen asynchronously, but the entire
201 // pipeline is also tested by NetworkErrorLoggingEndToEndTest.
202
TEST_P(ReportingHeaderParserTest,Invalid)203 TEST_P(ReportingHeaderParserTest, Invalid) {
204 static const struct {
205 const char* header_value;
206 const char* description;
207 } kInvalidHeaderTestCases[] = {
208 {"{\"max_age\":1, \"endpoints\": [{}]}", "missing url"},
209 {"{\"max_age\":1, \"endpoints\": [{\"url\":0}]}", "non-string url"},
210 {"{\"max_age\":1, \"endpoints\": [{\"url\":\"//scheme/relative\"}]}",
211 "scheme-relative url"},
212 {"{\"max_age\":1, \"endpoints\": [{\"url\":\"relative/path\"}]}",
213 "path relative url"},
214 {"{\"max_age\":1, \"endpoints\": [{\"url\":\"http://insecure/\"}]}",
215 "insecure url"},
216 {"{\"endpoints\": [{\"url\":\"https://endpoint/\"}]}", "missing max_age"},
217 {"{\"max_age\":\"\", \"endpoints\": [{\"url\":\"https://endpoint/\"}]}",
218 "non-integer max_age"},
219 {"{\"max_age\":-1, \"endpoints\": [{\"url\":\"https://endpoint/\"}]}",
220 "negative max_age"},
221 {"{\"max_age\":1, \"group\":0, "
222 "\"endpoints\": [{\"url\":\"https://endpoint/\"}]}",
223 "non-string group"},
224
225 // Note that a non-boolean include_subdomains field is *not* invalid, per
226 // the spec.
227
228 // Priority should be a nonnegative integer.
229 {"{\"max_age\":1, "
230 "\"endpoints\": [{\"url\":\"https://endpoint/\",\"priority\":\"\"}]}",
231 "non-integer priority"},
232 {"{\"max_age\":1, "
233 "\"endpoints\": [{\"url\":\"https://endpoint/\",\"priority\":-1}]}",
234 "negative priority"},
235
236 // Weight should be a non-negative integer.
237 {"{\"max_age\":1, "
238 "\"endpoints\": [{\"url\":\"https://endpoint/\",\"weight\":\"\"}]}",
239 "non-integer weight"},
240 {"{\"max_age\":1, "
241 "\"endpoints\": [{\"url\":\"https://endpoint/\",\"weight\":-1}]}",
242 "negative weight"},
243
244 {"[{\"max_age\":1, \"endpoints\": [{\"url\":\"https://a/\"}]},"
245 "{\"max_age\":1, \"endpoints\": [{\"url\":\"https://b/\"}]}]",
246 "wrapped in list"}};
247
248 base::HistogramTester histograms;
249 int invalid_case_count = 0;
250
251 for (const auto& test_case : kInvalidHeaderTestCases) {
252 ParseHeader(kNak_, kOrigin1_, test_case.header_value);
253 invalid_case_count++;
254
255 EXPECT_EQ(0u, cache()->GetEndpointCount())
256 << "Invalid Report-To header (" << test_case.description << ": \""
257 << test_case.header_value << "\") parsed as valid.";
258 histograms.ExpectBucketCount(
259 kReportingHeaderTypeHistogram,
260 ReportingHeaderParser::ReportingHeaderType::kReportToInvalid,
261 invalid_case_count);
262 if (mock_store()) {
263 mock_store()->Flush();
264 EXPECT_EQ(0, mock_store()->StoredEndpointsCount());
265 EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount());
266 }
267 }
268 histograms.ExpectBucketCount(
269 kReportingHeaderTypeHistogram,
270 ReportingHeaderParser::ReportingHeaderType::kReportTo, 0);
271 }
272
TEST_P(ReportingHeaderParserTest,Basic)273 TEST_P(ReportingHeaderParserTest, Basic) {
274 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
275 base::HistogramTester histograms;
276
277 std::string header =
278 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
279
280 ParseHeader(kNak_, kOrigin1_, header);
281 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
282 histograms.ExpectBucketCount(
283 kReportingHeaderTypeHistogram,
284 ReportingHeaderParser::ReportingHeaderType::kReportTo, 1);
285 EXPECT_TRUE(
286 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
287 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
288 EXPECT_EQ(1u, cache()->GetEndpointCount());
289 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
290 ASSERT_TRUE(endpoint);
291 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
292 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
293 EXPECT_EQ(kEndpoint1_, endpoint.info.url);
294 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
295 endpoint.info.priority);
296 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
297 endpoint.info.weight);
298
299 if (mock_store()) {
300 mock_store()->Flush();
301 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
302 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
303 MockPersistentReportingStore::CommandList expected_commands;
304 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
305 kGroupKey11_, kEndpoint1_);
306 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
307 kGroupKey11_);
308 EXPECT_THAT(mock_store()->GetAllCommands(),
309 testing::IsSupersetOf(expected_commands));
310 }
311 }
312
TEST_P(ReportingHeaderParserTest,PathAbsoluteURLEndpoint)313 TEST_P(ReportingHeaderParserTest, PathAbsoluteURLEndpoint) {
314 std::string header =
315 "{\"group\": \"group1\", \"max_age\":1, \"endpoints\": "
316 "[{\"url\":\"/path-absolute-url\"}]}";
317 base::HistogramTester histograms;
318
319 ParseHeader(kNak_, kOrigin1_, header);
320 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
321 histograms.ExpectBucketCount(
322 kReportingHeaderTypeHistogram,
323 ReportingHeaderParser::ReportingHeaderType::kReportTo, 1);
324 EXPECT_TRUE(
325 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
326 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
327 EXPECT_EQ(1u, cache()->GetEndpointCount());
328 ReportingEndpoint endpoint =
329 FindEndpointInCache(kGroupKey11_, kEndpointPathAbsolute_);
330 ASSERT_TRUE(endpoint);
331 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
332 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
333 EXPECT_EQ(kEndpointPathAbsolute_, endpoint.info.url);
334 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
335 endpoint.info.priority);
336 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
337 endpoint.info.weight);
338
339 if (mock_store()) {
340 mock_store()->Flush();
341 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
342 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
343 MockPersistentReportingStore::CommandList expected_commands;
344 expected_commands.emplace_back(
345 CommandType::ADD_REPORTING_ENDPOINT,
346 ReportingEndpoint(kGroupKey11_, ReportingEndpoint::EndpointInfo{
347 kEndpointPathAbsolute_}));
348 expected_commands.emplace_back(
349 CommandType::ADD_REPORTING_ENDPOINT_GROUP,
350 CachedReportingEndpointGroup(
351 kGroupKey11_, OriginSubdomains::DEFAULT /* irrelevant */,
352 base::Time() /* irrelevant */, base::Time() /* irrelevant */));
353 EXPECT_THAT(mock_store()->GetAllCommands(),
354 testing::IsSupersetOf(expected_commands));
355 }
356 }
357
TEST_P(ReportingHeaderParserTest,OmittedGroupName)358 TEST_P(ReportingHeaderParserTest, OmittedGroupName) {
359 ReportingEndpointGroupKey kGroupKey(kNak_, kOrigin1_, "default");
360 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
361 std::string header =
362 ConstructHeaderGroupString(MakeEndpointGroup(std::string(), endpoints));
363
364 ParseHeader(kNak_, kOrigin1_, header);
365 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
366 EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::DEFAULT));
367 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
368 EXPECT_EQ(1u, cache()->GetEndpointCount());
369 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey, kEndpoint1_);
370 ASSERT_TRUE(endpoint);
371 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
372 EXPECT_EQ("default", endpoint.group_key.group_name);
373 EXPECT_EQ(kEndpoint1_, endpoint.info.url);
374 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
375 endpoint.info.priority);
376 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
377 endpoint.info.weight);
378
379 if (mock_store()) {
380 mock_store()->Flush();
381 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
382 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
383 MockPersistentReportingStore::CommandList expected_commands;
384 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
385 kGroupKey, kEndpoint1_);
386 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
387 kGroupKey);
388 EXPECT_THAT(mock_store()->GetAllCommands(),
389 testing::IsSupersetOf(expected_commands));
390 }
391 }
392
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsTrue)393 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsTrue) {
394 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
395
396 std::string header = ConstructHeaderGroupString(
397 MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::INCLUDE));
398 ParseHeader(kNak_, kOrigin1_, header);
399
400 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
401 EXPECT_TRUE(
402 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::INCLUDE));
403 EXPECT_EQ(1u, cache()->GetEndpointCount());
404 EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_));
405
406 if (mock_store()) {
407 mock_store()->Flush();
408 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
409 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
410 MockPersistentReportingStore::CommandList expected_commands;
411 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
412 kGroupKey11_, kEndpoint1_);
413 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
414 kGroupKey11_);
415 EXPECT_THAT(mock_store()->GetAllCommands(),
416 testing::IsSupersetOf(expected_commands));
417 }
418 }
419
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsFalse)420 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsFalse) {
421 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
422
423 std::string header = ConstructHeaderGroupString(
424 MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::EXCLUDE),
425 false /* omit_defaults */);
426 ParseHeader(kNak_, kOrigin1_, header);
427
428 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
429 EXPECT_TRUE(
430 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::EXCLUDE));
431 EXPECT_EQ(1u, cache()->GetEndpointCount());
432 EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_));
433
434 if (mock_store()) {
435 mock_store()->Flush();
436 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
437 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
438 MockPersistentReportingStore::CommandList expected_commands;
439 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
440 kGroupKey11_, kEndpoint1_);
441 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
442 kGroupKey11_);
443 EXPECT_THAT(mock_store()->GetAllCommands(),
444 testing::IsSupersetOf(expected_commands));
445 }
446 }
447
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsEtldRejected)448 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsEtldRejected) {
449 ReportingEndpointGroupKey kGroupKey(kNak_, kOriginEtld_, kGroup1_);
450 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
451
452 std::string header = ConstructHeaderGroupString(
453 MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::INCLUDE));
454 ParseHeader(kNak_, kOriginEtld_, header);
455
456 EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
457 EXPECT_FALSE(
458 EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::INCLUDE));
459 EXPECT_EQ(0u, cache()->GetEndpointCount());
460 EXPECT_FALSE(EndpointExistsInCache(kGroupKey, kEndpoint1_));
461 }
462
TEST_P(ReportingHeaderParserTest,NonIncludeSubdomainsEtldAccepted)463 TEST_P(ReportingHeaderParserTest, NonIncludeSubdomainsEtldAccepted) {
464 ReportingEndpointGroupKey kGroupKey(kNak_, kOriginEtld_, kGroup1_);
465 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
466
467 std::string header = ConstructHeaderGroupString(
468 MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::EXCLUDE));
469 ParseHeader(kNak_, kOriginEtld_, header);
470
471 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
472 EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey, OriginSubdomains::EXCLUDE));
473 EXPECT_EQ(1u, cache()->GetEndpointCount());
474 EXPECT_TRUE(EndpointExistsInCache(kGroupKey, kEndpoint1_));
475 }
476
TEST_P(ReportingHeaderParserTest,IncludeSubdomainsNotBoolean)477 TEST_P(ReportingHeaderParserTest, IncludeSubdomainsNotBoolean) {
478 std::string header =
479 "{\"group\": \"" + kGroup1_ +
480 "\", "
481 "\"max_age\":86400, \"include_subdomains\": \"NotABoolean\", "
482 "\"endpoints\": [{\"url\":\"" +
483 kEndpoint1_.spec() + "\"}]}";
484 ParseHeader(kNak_, kOrigin1_, header);
485
486 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
487 EXPECT_TRUE(
488 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
489 EXPECT_EQ(1u, cache()->GetEndpointCount());
490 EXPECT_TRUE(EndpointExistsInCache(kGroupKey11_, kEndpoint1_));
491
492 if (mock_store()) {
493 mock_store()->Flush();
494 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
495 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
496 MockPersistentReportingStore::CommandList expected_commands;
497 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
498 kGroupKey11_, kEndpoint1_);
499 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
500 kGroupKey11_);
501 EXPECT_THAT(mock_store()->GetAllCommands(),
502 testing::IsSupersetOf(expected_commands));
503 }
504 }
505
TEST_P(ReportingHeaderParserTest,NonDefaultPriority)506 TEST_P(ReportingHeaderParserTest, NonDefaultPriority) {
507 const int kNonDefaultPriority = 10;
508 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {
509 {kEndpoint1_, kNonDefaultPriority}};
510
511 std::string header =
512 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
513 ParseHeader(kNak_, kOrigin1_, header);
514
515 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
516 EXPECT_TRUE(
517 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
518 EXPECT_EQ(1u, cache()->GetEndpointCount());
519 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
520 ASSERT_TRUE(endpoint);
521 EXPECT_EQ(kNonDefaultPriority, endpoint.info.priority);
522 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
523 endpoint.info.weight);
524
525 if (mock_store()) {
526 mock_store()->Flush();
527 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
528 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
529 MockPersistentReportingStore::CommandList expected_commands;
530 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
531 kGroupKey11_, kEndpoint1_);
532 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
533 kGroupKey11_);
534 EXPECT_THAT(mock_store()->GetAllCommands(),
535 testing::IsSupersetOf(expected_commands));
536 }
537 }
538
TEST_P(ReportingHeaderParserTest,NonDefaultWeight)539 TEST_P(ReportingHeaderParserTest, NonDefaultWeight) {
540 const int kNonDefaultWeight = 10;
541 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {
542 {kEndpoint1_, ReportingEndpoint::EndpointInfo::kDefaultPriority,
543 kNonDefaultWeight}};
544
545 std::string header =
546 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
547 ParseHeader(kNak_, kOrigin1_, header);
548
549 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
550 EXPECT_TRUE(
551 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
552 EXPECT_EQ(1u, cache()->GetEndpointCount());
553 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
554 ASSERT_TRUE(endpoint);
555 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
556 endpoint.info.priority);
557 EXPECT_EQ(kNonDefaultWeight, endpoint.info.weight);
558
559 if (mock_store()) {
560 mock_store()->Flush();
561 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
562 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
563 MockPersistentReportingStore::CommandList expected_commands;
564 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
565 kGroupKey11_, kEndpoint1_);
566 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
567 kGroupKey11_);
568 EXPECT_THAT(mock_store()->GetAllCommands(),
569 testing::IsSupersetOf(expected_commands));
570 }
571 }
572
TEST_P(ReportingHeaderParserTest,MaxAge)573 TEST_P(ReportingHeaderParserTest, MaxAge) {
574 const int kMaxAgeSecs = 100;
575 base::TimeDelta ttl = base::Seconds(kMaxAgeSecs);
576 base::Time expires = clock()->Now() + ttl;
577
578 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
579
580 std::string header = ConstructHeaderGroupString(
581 MakeEndpointGroup(kGroup1_, endpoints, OriginSubdomains::DEFAULT, ttl));
582
583 ParseHeader(kNak_, kOrigin1_, header);
584 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
585 EXPECT_TRUE(EndpointGroupExistsInCache(kGroupKey11_,
586 OriginSubdomains::DEFAULT, expires));
587
588 if (mock_store()) {
589 mock_store()->Flush();
590 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
591 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
592 MockPersistentReportingStore::CommandList expected_commands;
593 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
594 kGroupKey11_, kEndpoint1_);
595 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
596 kGroupKey11_);
597 EXPECT_THAT(mock_store()->GetAllCommands(),
598 testing::IsSupersetOf(expected_commands));
599 }
600 }
601
TEST_P(ReportingHeaderParserTest,MultipleEndpointsSameGroup)602 TEST_P(ReportingHeaderParserTest, MultipleEndpointsSameGroup) {
603 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_},
604 {kEndpoint2_}};
605 std::string header =
606 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
607
608 ParseHeader(kNak_, kOrigin1_, header);
609 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
610 EXPECT_TRUE(
611 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
612 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
613 EXPECT_EQ(2u, cache()->GetEndpointCount());
614 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
615 ASSERT_TRUE(endpoint);
616 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
617 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
618 EXPECT_EQ(kEndpoint1_, endpoint.info.url);
619 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
620 endpoint.info.priority);
621 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
622 endpoint.info.weight);
623
624 ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey11_, kEndpoint2_);
625 ASSERT_TRUE(endpoint2);
626 EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin);
627 EXPECT_EQ(kGroup1_, endpoint2.group_key.group_name);
628 EXPECT_EQ(kEndpoint2_, endpoint2.info.url);
629 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
630 endpoint2.info.priority);
631 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
632 endpoint2.info.weight);
633
634 if (mock_store()) {
635 mock_store()->Flush();
636 EXPECT_EQ(2, mock_store()->StoredEndpointsCount());
637 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
638 MockPersistentReportingStore::CommandList expected_commands;
639 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
640 kGroupKey11_, kEndpoint1_);
641 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
642 kGroupKey11_, kEndpoint2_);
643 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
644 kGroupKey11_);
645 EXPECT_THAT(mock_store()->GetAllCommands(),
646 testing::IsSupersetOf(expected_commands));
647 }
648 }
649
TEST_P(ReportingHeaderParserTest,MultipleEndpointsDifferentGroups)650 TEST_P(ReportingHeaderParserTest, MultipleEndpointsDifferentGroups) {
651 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
652 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}};
653 std::string header =
654 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
655 ", " +
656 ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2));
657
658 ParseHeader(kNak_, kOrigin1_, header);
659 EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
660 EXPECT_TRUE(
661 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
662 EXPECT_TRUE(
663 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
664 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
665
666 EXPECT_EQ(2u, cache()->GetEndpointCount());
667 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
668 ASSERT_TRUE(endpoint);
669 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
670 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
671 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
672 endpoint.info.priority);
673 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
674 endpoint.info.weight);
675
676 ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey12_, kEndpoint1_);
677 ASSERT_TRUE(endpoint2);
678 EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin);
679 EXPECT_EQ(kGroup2_, endpoint2.group_key.group_name);
680 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
681 endpoint2.info.priority);
682 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
683 endpoint2.info.weight);
684
685 if (mock_store()) {
686 mock_store()->Flush();
687 EXPECT_EQ(2, mock_store()->StoredEndpointsCount());
688 EXPECT_EQ(2, mock_store()->StoredEndpointGroupsCount());
689 MockPersistentReportingStore::CommandList expected_commands;
690 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
691 kGroupKey11_, kEndpoint1_);
692 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
693 kGroupKey12_, kEndpoint1_);
694 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
695 kGroupKey11_);
696 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
697 kGroupKey12_);
698 EXPECT_THAT(mock_store()->GetAllCommands(),
699 testing::IsSupersetOf(expected_commands));
700 }
701 }
702
TEST_P(ReportingHeaderParserTest,MultipleHeadersFromDifferentOrigins)703 TEST_P(ReportingHeaderParserTest, MultipleHeadersFromDifferentOrigins) {
704 // First origin sets a header with two endpoints in the same group.
705 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_},
706 {kEndpoint2_}};
707 std::string header1 =
708 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1));
709 ParseHeader(kNak_, kOrigin1_, header1);
710
711 // Second origin has two endpoint groups.
712 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_}};
713 std::vector<ReportingEndpoint::EndpointInfo> endpoints3 = {{kEndpoint2_}};
714 std::string header2 =
715 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)) +
716 ", " +
717 ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints3));
718 ParseHeader(kNak_, kOrigin2_, header2);
719
720 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
721 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin2_));
722
723 EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting());
724 EXPECT_TRUE(
725 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
726 EXPECT_TRUE(
727 EndpointGroupExistsInCache(kGroupKey21_, OriginSubdomains::DEFAULT));
728 EXPECT_TRUE(
729 EndpointGroupExistsInCache(kGroupKey22_, OriginSubdomains::DEFAULT));
730
731 EXPECT_EQ(4u, cache()->GetEndpointCount());
732 EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
733 EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
734 EXPECT_TRUE(FindEndpointInCache(kGroupKey21_, kEndpoint1_));
735 EXPECT_TRUE(FindEndpointInCache(kGroupKey22_, kEndpoint2_));
736
737 if (mock_store()) {
738 mock_store()->Flush();
739 EXPECT_EQ(4, mock_store()->StoredEndpointsCount());
740 EXPECT_EQ(3, mock_store()->StoredEndpointGroupsCount());
741 MockPersistentReportingStore::CommandList expected_commands;
742 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
743 kGroupKey11_, kEndpoint1_);
744 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
745 kGroupKey11_, kEndpoint2_);
746 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
747 kGroupKey21_, kEndpoint1_);
748 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
749 kGroupKey22_, kEndpoint2_);
750 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
751 kGroupKey11_);
752 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
753 kGroupKey21_);
754 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
755 kGroupKey22_);
756 EXPECT_THAT(mock_store()->GetAllCommands(),
757 testing::IsSupersetOf(expected_commands));
758 }
759 }
760
761 // Test that each combination of NAK, origin, and group name is considered
762 // distinct.
763 // See also: ReportingCacheTest.ClientsKeyedByEndpointGroupKey
TEST_P(ReportingHeaderParserTest,EndpointGroupKey)764 TEST_P(ReportingHeaderParserTest, EndpointGroupKey) {
765 // Raise the endpoint limits for this test.
766 ReportingPolicy policy;
767 policy.max_endpoints_per_origin = 5; // This test should use 4.
768 policy.max_endpoint_count = 20; // This test should use 16.
769 UsePolicy(policy);
770
771 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_},
772 {kEndpoint2_}};
773 std::string header1 =
774 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
775 ", " +
776 ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints1));
777
778 const ReportingEndpointGroupKey kOtherGroupKey11 =
779 ReportingEndpointGroupKey(kOtherNak_, kOrigin1_, kGroup1_);
780 const ReportingEndpointGroupKey kOtherGroupKey21 =
781 ReportingEndpointGroupKey(kOtherNak_, kOrigin2_, kGroup1_);
782 const ReportingEndpointGroupKey kOtherGroupKey12 =
783 ReportingEndpointGroupKey(kOtherNak_, kOrigin1_, kGroup2_);
784 const ReportingEndpointGroupKey kOtherGroupKey22 =
785 ReportingEndpointGroupKey(kOtherNak_, kOrigin2_, kGroup2_);
786
787 const struct {
788 NetworkAnonymizationKey network_anonymization_key;
789 GURL url;
790 ReportingEndpointGroupKey group1_key;
791 ReportingEndpointGroupKey group2_key;
792 } kHeaderSources[] = {
793 {kNak_, kUrl1_, kGroupKey11_, kGroupKey12_},
794 {kNak_, kUrl2_, kGroupKey21_, kGroupKey22_},
795 {kOtherNak_, kUrl1_, kOtherGroupKey11, kOtherGroupKey12},
796 {kOtherNak_, kUrl2_, kOtherGroupKey21, kOtherGroupKey22},
797 };
798
799 size_t endpoint_group_count = 0u;
800 size_t endpoint_count = 0u;
801 MockPersistentReportingStore::CommandList expected_commands;
802
803 // Set 2 endpoints in each of 2 groups for each of 2x2 combinations of
804 // (NAK, origin).
805 for (const auto& source : kHeaderSources) {
806 // Verify pre-parsing state
807 EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint1_));
808 EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint2_));
809 EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_));
810 EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint2_));
811 EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key,
812 OriginSubdomains::DEFAULT));
813 EXPECT_FALSE(EndpointGroupExistsInCache(source.group2_key,
814 OriginSubdomains::DEFAULT));
815
816 ParseHeader(source.network_anonymization_key,
817 url::Origin::Create(source.url), header1);
818 endpoint_group_count += 2u;
819 endpoint_count += 4u;
820 EXPECT_EQ(endpoint_group_count, cache()->GetEndpointGroupCountForTesting());
821 EXPECT_EQ(endpoint_count, cache()->GetEndpointCount());
822
823 // Verify post-parsing state
824 EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint1_));
825 EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint2_));
826 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_));
827 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_));
828 EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key,
829 OriginSubdomains::DEFAULT));
830 EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
831 OriginSubdomains::DEFAULT));
832
833 if (mock_store()) {
834 mock_store()->Flush();
835 EXPECT_EQ(static_cast<int>(endpoint_count),
836 mock_store()->StoredEndpointsCount());
837 EXPECT_EQ(static_cast<int>(endpoint_group_count),
838 mock_store()->StoredEndpointGroupsCount());
839 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
840 source.group1_key, kEndpoint1_);
841 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
842 source.group1_key, kEndpoint2_);
843 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
844 source.group1_key);
845 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
846 source.group2_key, kEndpoint1_);
847 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
848 source.group2_key, kEndpoint2_);
849 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
850 source.group2_key);
851 EXPECT_THAT(mock_store()->GetAllCommands(),
852 testing::IsSupersetOf(expected_commands));
853 }
854 }
855
856 // Check that expected data is present in the ReportingCache at the end.
857 for (const auto& source : kHeaderSources) {
858 EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint1_));
859 EXPECT_TRUE(FindEndpointInCache(source.group1_key, kEndpoint2_));
860 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_));
861 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_));
862 EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key,
863 OriginSubdomains::DEFAULT));
864 EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
865 OriginSubdomains::DEFAULT));
866 EXPECT_TRUE(cache()->ClientExistsForTesting(
867 source.network_anonymization_key, url::Origin::Create(source.url)));
868 }
869
870 // Test updating existing configurations
871
872 // This removes endpoint 1, updates the priority of endpoint 2, and adds
873 // endpoint 3.
874 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_, 2},
875 {kEndpoint3_}};
876 // Removes group 1, updates include_subdomains for group 2.
877 std::string header2 = ConstructHeaderGroupString(
878 MakeEndpointGroup(kGroup2_, endpoints2, OriginSubdomains::INCLUDE));
879
880 for (const auto& source : kHeaderSources) {
881 // Verify pre-update state
882 EXPECT_TRUE(EndpointGroupExistsInCache(source.group1_key,
883 OriginSubdomains::DEFAULT));
884 EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
885 OriginSubdomains::DEFAULT));
886 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint1_));
887 ReportingEndpoint endpoint =
888 FindEndpointInCache(source.group2_key, kEndpoint2_);
889 EXPECT_TRUE(endpoint);
890 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
891 endpoint.info.priority);
892 EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint3_));
893
894 ParseHeader(source.network_anonymization_key,
895 url::Origin::Create(source.url), header2);
896 endpoint_group_count--;
897 endpoint_count -= 2;
898 EXPECT_EQ(endpoint_group_count, cache()->GetEndpointGroupCountForTesting());
899 EXPECT_EQ(endpoint_count, cache()->GetEndpointCount());
900
901 // Verify post-update state
902 EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key,
903 OriginSubdomains::DEFAULT));
904 EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
905 OriginSubdomains::INCLUDE));
906 EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_));
907 endpoint = FindEndpointInCache(source.group2_key, kEndpoint2_);
908 EXPECT_TRUE(endpoint);
909 EXPECT_EQ(2, endpoint.info.priority);
910 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint3_));
911
912 if (mock_store()) {
913 mock_store()->Flush();
914 EXPECT_EQ(static_cast<int>(endpoint_count),
915 mock_store()->StoredEndpointsCount());
916 EXPECT_EQ(static_cast<int>(endpoint_group_count),
917 mock_store()->StoredEndpointGroupsCount());
918 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
919 source.group1_key, kEndpoint1_);
920 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
921 source.group1_key, kEndpoint2_);
922 expected_commands.emplace_back(
923 CommandType::DELETE_REPORTING_ENDPOINT_GROUP, source.group1_key);
924 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
925 source.group2_key, kEndpoint1_);
926 expected_commands.emplace_back(
927 CommandType::UPDATE_REPORTING_ENDPOINT_DETAILS, source.group2_key,
928 kEndpoint2_);
929 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
930 source.group2_key, kEndpoint3_);
931 expected_commands.emplace_back(
932 CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS,
933 source.group2_key);
934 EXPECT_THAT(mock_store()->GetAllCommands(),
935 testing::IsSupersetOf(expected_commands));
936 }
937 }
938
939 // Check that expected data is present in the ReportingCache at the end.
940 for (const auto& source : kHeaderSources) {
941 EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint1_));
942 EXPECT_FALSE(FindEndpointInCache(source.group1_key, kEndpoint2_));
943 EXPECT_FALSE(FindEndpointInCache(source.group2_key, kEndpoint1_));
944 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint2_));
945 EXPECT_TRUE(FindEndpointInCache(source.group2_key, kEndpoint3_));
946 EXPECT_FALSE(EndpointGroupExistsInCache(source.group1_key,
947 OriginSubdomains::DEFAULT));
948 EXPECT_TRUE(EndpointGroupExistsInCache(source.group2_key,
949 OriginSubdomains::INCLUDE));
950 EXPECT_TRUE(cache()->ClientExistsForTesting(
951 source.network_anonymization_key, url::Origin::Create(source.url)));
952 }
953 }
954
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsMultipleGroupsOfSameName)955 TEST_P(ReportingHeaderParserTest,
956 HeaderErroneouslyContainsMultipleGroupsOfSameName) {
957 // Add a preexisting header to test that a header with multiple groups of the
958 // same name is treated as if it specified a single group with the combined
959 // set of specified endpoints. In particular, it must overwrite/update any
960 // preexisting group all at once. See https://crbug.com/1116529.
961 std::vector<ReportingEndpoint::EndpointInfo> preexisting = {{kEndpoint1_}};
962 std::string preexisting_header =
963 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, preexisting));
964
965 ParseHeader(kNak_, kOrigin1_, preexisting_header);
966 EXPECT_TRUE(
967 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
968 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
969 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
970 EXPECT_EQ(1u, cache()->GetEndpointCount());
971 ReportingEndpoint endpoint = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
972 ASSERT_TRUE(endpoint);
973
974 if (mock_store()) {
975 mock_store()->Flush();
976 EXPECT_EQ(1, mock_store()->StoredEndpointsCount());
977 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
978 MockPersistentReportingStore::CommandList expected_commands;
979 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
980 kGroupKey11_, kEndpoint1_);
981 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
982 kGroupKey11_);
983 EXPECT_THAT(mock_store()->GetAllCommands(),
984 testing::IsSupersetOf(expected_commands));
985 // Reset commands so we can check that the next part, adding the header with
986 // duplicate groups, does not cause clearing of preexisting endpoints twice.
987 mock_store()->ClearCommands();
988 }
989
990 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
991 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}};
992 std::string duplicate_groups_header =
993 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
994 ", " +
995 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2));
996
997 ParseHeader(kNak_, kOrigin1_, duplicate_groups_header);
998 // Result is as if they set the two groups with the same name as one group.
999 EXPECT_TRUE(
1000 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1001 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1002
1003 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1004
1005 EXPECT_EQ(2u, cache()->GetEndpointCount());
1006 ReportingEndpoint endpoint1 = FindEndpointInCache(kGroupKey11_, kEndpoint1_);
1007 ASSERT_TRUE(endpoint);
1008 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
1009 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
1010 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1011 endpoint.info.priority);
1012 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1013 endpoint.info.weight);
1014
1015 ReportingEndpoint endpoint2 = FindEndpointInCache(kGroupKey11_, kEndpoint2_);
1016 ASSERT_TRUE(endpoint2);
1017 EXPECT_EQ(kOrigin1_, endpoint2.group_key.origin);
1018 EXPECT_EQ(kGroup1_, endpoint2.group_key.group_name);
1019 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1020 endpoint2.info.priority);
1021 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1022 endpoint2.info.weight);
1023
1024 if (mock_store()) {
1025 mock_store()->Flush();
1026 EXPECT_EQ(2, mock_store()->StoredEndpointsCount());
1027 EXPECT_EQ(1, mock_store()->StoredEndpointGroupsCount());
1028 MockPersistentReportingStore::CommandList expected_commands;
1029 expected_commands.emplace_back(
1030 CommandType::UPDATE_REPORTING_ENDPOINT_DETAILS, kGroupKey11_,
1031 kEndpoint1_);
1032 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1033 kGroupKey11_, kEndpoint2_);
1034 expected_commands.emplace_back(
1035 CommandType::UPDATE_REPORTING_ENDPOINT_GROUP_DETAILS, kGroupKey11_);
1036 MockPersistentReportingStore::CommandList actual_commands =
1037 mock_store()->GetAllCommands();
1038 EXPECT_THAT(actual_commands, testing::IsSupersetOf(expected_commands));
1039 for (const auto& command : actual_commands) {
1040 EXPECT_NE(CommandType::DELETE_REPORTING_ENDPOINT, command.type);
1041 EXPECT_NE(CommandType::DELETE_REPORTING_ENDPOINT_GROUP, command.type);
1042
1043 // The endpoint with URL kEndpoint1_ is only ever updated, not added anew.
1044 EXPECT_NE(
1045 MockPersistentReportingStore::Command(
1046 CommandType::ADD_REPORTING_ENDPOINT, kGroupKey11_, kEndpoint1_),
1047 command);
1048 // The group is only ever updated, not added anew.
1049 EXPECT_NE(MockPersistentReportingStore::Command(
1050 CommandType::ADD_REPORTING_ENDPOINT_GROUP, kGroupKey11_),
1051 command);
1052 }
1053 }
1054 }
1055
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsGroupsWithRedundantEndpoints)1056 TEST_P(ReportingHeaderParserTest,
1057 HeaderErroneouslyContainsGroupsWithRedundantEndpoints) {
1058 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_},
1059 {kEndpoint1_}};
1060 std::string header =
1061 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1062 ParseHeader(kNak_, kOrigin1_, header);
1063
1064 // We should dedupe the identical endpoint URLs.
1065 EXPECT_EQ(1u, cache()->GetEndpointCount());
1066 ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1067
1068 EXPECT_TRUE(
1069 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1070 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1071
1072 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1073 }
1074
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsMultipleGroupsOfSameNameAndEndpoints)1075 TEST_P(ReportingHeaderParserTest,
1076 HeaderErroneouslyContainsMultipleGroupsOfSameNameAndEndpoints) {
1077 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
1078 std::string header =
1079 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints)) +
1080 ", " + ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1081 ParseHeader(kNak_, kOrigin1_, header);
1082
1083 // We should dedupe the identical endpoint URLs, even when they're in
1084 // different group.
1085 EXPECT_EQ(1u, cache()->GetEndpointCount());
1086 ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1087
1088 EXPECT_TRUE(
1089 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1090 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1091
1092 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1093 }
1094
TEST_P(ReportingHeaderParserTest,HeaderErroneouslyContainsGroupsOfSameNameAndOverlappingEndpoints)1095 TEST_P(ReportingHeaderParserTest,
1096 HeaderErroneouslyContainsGroupsOfSameNameAndOverlappingEndpoints) {
1097 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_},
1098 {kEndpoint2_}};
1099 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint1_},
1100 {kEndpoint3_}};
1101 std::string header =
1102 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
1103 ", " +
1104 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2));
1105 ParseHeader(kNak_, kOrigin1_, header);
1106
1107 // We should dedupe the identical endpoint URLs, even when they're in
1108 // different group.
1109 EXPECT_EQ(3u, cache()->GetEndpointCount());
1110 ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1111 ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
1112 ASSERT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint3_));
1113
1114 EXPECT_TRUE(
1115 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1116 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1117
1118 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1119 }
1120
TEST_P(ReportingHeaderParserTest,OverwriteOldHeader)1121 TEST_P(ReportingHeaderParserTest, OverwriteOldHeader) {
1122 // First, the origin sets a header with two endpoints in the same group.
1123 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {
1124 {kEndpoint1_, 10 /* priority */}, {kEndpoint2_}};
1125 std::string header1 =
1126 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1));
1127 ParseHeader(kNak_, kOrigin1_, header1);
1128
1129 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1130 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1131 EXPECT_TRUE(
1132 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1133 EXPECT_EQ(2u, cache()->GetEndpointCount());
1134 EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1135 EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
1136 if (mock_store()) {
1137 mock_store()->Flush();
1138 EXPECT_EQ(2,
1139 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1140 EXPECT_EQ(1, mock_store()->CountCommands(
1141 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1142 MockPersistentReportingStore::CommandList expected_commands;
1143 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1144 kGroupKey11_, kEndpoint1_);
1145 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1146 kGroupKey11_, kEndpoint2_);
1147 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1148 kGroupKey11_);
1149 EXPECT_THAT(mock_store()->GetAllCommands(),
1150 testing::IsSupersetOf(expected_commands));
1151 }
1152
1153 // Second header from the same origin should overwrite the previous one.
1154 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {
1155 // This endpoint should update the priority of the existing one.
1156 {kEndpoint1_, 20 /* priority */}};
1157 // The second endpoint in this group will be deleted.
1158 // This group is new.
1159 std::vector<ReportingEndpoint::EndpointInfo> endpoints3 = {{kEndpoint2_}};
1160 std::string header2 =
1161 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints2)) +
1162 ", " +
1163 ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints3));
1164 ParseHeader(kNak_, kOrigin1_, header2);
1165
1166 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1167
1168 EXPECT_TRUE(
1169 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1170 EXPECT_TRUE(
1171 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1172
1173 EXPECT_EQ(2u, cache()->GetEndpointCount());
1174 EXPECT_TRUE(FindEndpointInCache(kGroupKey11_, kEndpoint1_));
1175 EXPECT_EQ(20, FindEndpointInCache(kGroupKey11_, kEndpoint1_).info.priority);
1176 EXPECT_FALSE(FindEndpointInCache(kGroupKey11_, kEndpoint2_));
1177 EXPECT_TRUE(FindEndpointInCache(kGroupKey12_, kEndpoint2_));
1178 if (mock_store()) {
1179 mock_store()->Flush();
1180 EXPECT_EQ(2 + 1,
1181 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1182 EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1183 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1184 EXPECT_EQ(
1185 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1186 MockPersistentReportingStore::CommandList expected_commands;
1187 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1188 kGroupKey12_, kEndpoint2_);
1189 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1190 kGroupKey12_);
1191 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1192 kGroupKey11_, kEndpoint2_);
1193 EXPECT_THAT(mock_store()->GetAllCommands(),
1194 testing::IsSupersetOf(expected_commands));
1195 }
1196 }
1197
TEST_P(ReportingHeaderParserTest,OverwriteOldHeaderWithCompletelyNew)1198 TEST_P(ReportingHeaderParserTest, OverwriteOldHeaderWithCompletelyNew) {
1199 ReportingEndpointGroupKey kGroupKey1(kNak_, kOrigin1_, "1");
1200 ReportingEndpointGroupKey kGroupKey2(kNak_, kOrigin1_, "2");
1201 ReportingEndpointGroupKey kGroupKey3(kNak_, kOrigin1_, "3");
1202 ReportingEndpointGroupKey kGroupKey4(kNak_, kOrigin1_, "4");
1203 ReportingEndpointGroupKey kGroupKey5(kNak_, kOrigin1_, "5");
1204 std::vector<ReportingEndpoint::EndpointInfo> endpoints1_1 = {{MakeURL(10)},
1205 {MakeURL(11)}};
1206 std::vector<ReportingEndpoint::EndpointInfo> endpoints2_1 = {{MakeURL(20)},
1207 {MakeURL(21)}};
1208 std::vector<ReportingEndpoint::EndpointInfo> endpoints3_1 = {{MakeURL(30)},
1209 {MakeURL(31)}};
1210 std::string header1 =
1211 ConstructHeaderGroupString(MakeEndpointGroup("1", endpoints1_1)) + ", " +
1212 ConstructHeaderGroupString(MakeEndpointGroup("2", endpoints2_1)) + ", " +
1213 ConstructHeaderGroupString(MakeEndpointGroup("3", endpoints3_1));
1214 ParseHeader(kNak_, kOrigin1_, header1);
1215 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1216 EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting());
1217 EXPECT_TRUE(
1218 EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT));
1219 EXPECT_TRUE(
1220 EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT));
1221 EXPECT_TRUE(
1222 EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT));
1223 EXPECT_EQ(6u, cache()->GetEndpointCount());
1224 if (mock_store()) {
1225 mock_store()->Flush();
1226 EXPECT_EQ(6,
1227 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1228 EXPECT_EQ(3, mock_store()->CountCommands(
1229 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1230 MockPersistentReportingStore::CommandList expected_commands;
1231 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1232 kGroupKey1, endpoints1_1[0].url);
1233 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1234 kGroupKey1, endpoints1_1[1].url);
1235 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1236 kGroupKey2, endpoints2_1[0].url);
1237 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1238 kGroupKey2, endpoints2_1[1].url);
1239 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1240 kGroupKey3, endpoints3_1[0].url);
1241 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1242 kGroupKey3, endpoints3_1[1].url);
1243 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1244 kGroupKey1);
1245 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1246 kGroupKey2);
1247 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1248 kGroupKey3);
1249 EXPECT_THAT(mock_store()->GetAllCommands(),
1250 testing::IsSupersetOf(expected_commands));
1251 }
1252
1253 // Replace endpoints in each group with completely new endpoints.
1254 std::vector<ReportingEndpoint::EndpointInfo> endpoints1_2 = {{MakeURL(12)}};
1255 std::vector<ReportingEndpoint::EndpointInfo> endpoints2_2 = {{MakeURL(22)}};
1256 std::vector<ReportingEndpoint::EndpointInfo> endpoints3_2 = {{MakeURL(32)}};
1257 std::string header2 =
1258 ConstructHeaderGroupString(MakeEndpointGroup("1", endpoints1_2)) + ", " +
1259 ConstructHeaderGroupString(MakeEndpointGroup("2", endpoints2_2)) + ", " +
1260 ConstructHeaderGroupString(MakeEndpointGroup("3", endpoints3_2));
1261 ParseHeader(kNak_, kOrigin1_, header2);
1262 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1263 EXPECT_EQ(3u, cache()->GetEndpointGroupCountForTesting());
1264 EXPECT_TRUE(
1265 EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT));
1266 EXPECT_TRUE(
1267 EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT));
1268 EXPECT_TRUE(
1269 EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT));
1270 EXPECT_EQ(3u, cache()->GetEndpointCount());
1271 EXPECT_TRUE(FindEndpointInCache(kGroupKey1, MakeURL(12)));
1272 EXPECT_FALSE(FindEndpointInCache(kGroupKey1, MakeURL(10)));
1273 EXPECT_FALSE(FindEndpointInCache(kGroupKey1, MakeURL(11)));
1274 EXPECT_TRUE(FindEndpointInCache(kGroupKey2, MakeURL(22)));
1275 EXPECT_FALSE(FindEndpointInCache(kGroupKey2, MakeURL(20)));
1276 EXPECT_FALSE(FindEndpointInCache(kGroupKey2, MakeURL(21)));
1277 EXPECT_TRUE(FindEndpointInCache(kGroupKey3, MakeURL(32)));
1278 EXPECT_FALSE(FindEndpointInCache(kGroupKey3, MakeURL(30)));
1279 EXPECT_FALSE(FindEndpointInCache(kGroupKey3, MakeURL(31)));
1280 if (mock_store()) {
1281 mock_store()->Flush();
1282 EXPECT_EQ(6 + 3,
1283 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1284 EXPECT_EQ(3, mock_store()->CountCommands(
1285 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1286 EXPECT_EQ(
1287 6, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1288 EXPECT_EQ(0, mock_store()->CountCommands(
1289 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1290 MockPersistentReportingStore::CommandList expected_commands;
1291 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1292 kGroupKey1, endpoints1_2[0].url);
1293 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1294 kGroupKey2, endpoints2_2[0].url);
1295 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1296 kGroupKey3, endpoints3_2[0].url);
1297 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1298 kGroupKey1, endpoints1_1[0].url);
1299 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1300 kGroupKey1, endpoints1_1[1].url);
1301 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1302 kGroupKey2, endpoints2_1[0].url);
1303 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1304 kGroupKey2, endpoints2_1[1].url);
1305 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1306 kGroupKey3, endpoints3_1[0].url);
1307 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1308 kGroupKey3, endpoints3_1[1].url);
1309 EXPECT_THAT(mock_store()->GetAllCommands(),
1310 testing::IsSupersetOf(expected_commands));
1311 }
1312
1313 // Replace all the groups with completely new groups.
1314 std::vector<ReportingEndpoint::EndpointInfo> endpoints4_3 = {{MakeURL(40)}};
1315 std::vector<ReportingEndpoint::EndpointInfo> endpoints5_3 = {{MakeURL(50)}};
1316 std::string header3 =
1317 ConstructHeaderGroupString(MakeEndpointGroup("4", endpoints4_3)) + ", " +
1318 ConstructHeaderGroupString(MakeEndpointGroup("5", endpoints5_3));
1319 ParseHeader(kNak_, kOrigin1_, header3);
1320 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1321 EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
1322 EXPECT_TRUE(
1323 EndpointGroupExistsInCache(kGroupKey4, OriginSubdomains::DEFAULT));
1324 EXPECT_TRUE(
1325 EndpointGroupExistsInCache(kGroupKey4, OriginSubdomains::DEFAULT));
1326 EXPECT_FALSE(
1327 EndpointGroupExistsInCache(kGroupKey1, OriginSubdomains::DEFAULT));
1328 EXPECT_FALSE(
1329 EndpointGroupExistsInCache(kGroupKey2, OriginSubdomains::DEFAULT));
1330 EXPECT_FALSE(
1331 EndpointGroupExistsInCache(kGroupKey3, OriginSubdomains::DEFAULT));
1332 EXPECT_EQ(2u, cache()->GetEndpointCount());
1333 if (mock_store()) {
1334 mock_store()->Flush();
1335 EXPECT_EQ(6 + 3 + 2,
1336 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1337 EXPECT_EQ(3 + 2, mock_store()->CountCommands(
1338 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1339 EXPECT_EQ(6 + 3, mock_store()->CountCommands(
1340 CommandType::DELETE_REPORTING_ENDPOINT));
1341 EXPECT_EQ(3, mock_store()->CountCommands(
1342 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1343 MockPersistentReportingStore::CommandList expected_commands;
1344 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1345 kGroupKey4, endpoints4_3[0].url);
1346 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1347 kGroupKey5, endpoints5_3[0].url);
1348 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1349 kGroupKey4);
1350 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1351 kGroupKey5);
1352 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1353 kGroupKey1, endpoints1_2[0].url);
1354 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1355 kGroupKey2, endpoints2_2[0].url);
1356 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1357 kGroupKey3, endpoints3_2[0].url);
1358 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1359 kGroupKey1);
1360 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1361 kGroupKey2);
1362 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1363 kGroupKey3);
1364 EXPECT_THAT(mock_store()->GetAllCommands(),
1365 testing::IsSupersetOf(expected_commands));
1366 }
1367 }
1368
TEST_P(ReportingHeaderParserTest,ZeroMaxAgeRemovesEndpointGroup)1369 TEST_P(ReportingHeaderParserTest, ZeroMaxAgeRemovesEndpointGroup) {
1370 // Without a pre-existing client, max_age: 0 should do nothing.
1371 ASSERT_EQ(0u, cache()->GetEndpointCount());
1372 ParseHeader(kNak_, kOrigin1_,
1373 "{\"endpoints\":[{\"url\":\"" + kEndpoint1_.spec() +
1374 "\"}],\"max_age\":0}");
1375 EXPECT_EQ(0u, cache()->GetEndpointCount());
1376 if (mock_store()) {
1377 mock_store()->Flush();
1378 EXPECT_EQ(0,
1379 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1380 EXPECT_EQ(0, mock_store()->CountCommands(
1381 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1382 }
1383
1384 // Set a header with two endpoint groups.
1385 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
1386 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}};
1387 std::string header1 =
1388 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
1389 ", " +
1390 ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2));
1391 ParseHeader(kNak_, kOrigin1_, header1);
1392
1393 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1394 EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
1395 EXPECT_TRUE(
1396 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1397 EXPECT_TRUE(
1398 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1399 EXPECT_EQ(2u, cache()->GetEndpointCount());
1400 if (mock_store()) {
1401 mock_store()->Flush();
1402 EXPECT_EQ(2,
1403 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1404 EXPECT_EQ(2, mock_store()->CountCommands(
1405 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1406 MockPersistentReportingStore::CommandList expected_commands;
1407 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1408 kGroupKey11_, kEndpoint1_);
1409 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1410 kGroupKey12_, kEndpoint2_);
1411 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1412 kGroupKey11_);
1413 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1414 kGroupKey12_);
1415 EXPECT_THAT(mock_store()->GetAllCommands(),
1416 testing::IsSupersetOf(expected_commands));
1417 }
1418
1419 // Set another header with max_age: 0 to delete one of the groups.
1420 std::string header2 =
1421 ConstructHeaderGroupString(MakeEndpointGroup(
1422 kGroup1_, endpoints1, OriginSubdomains::DEFAULT, base::Seconds(0))) +
1423 ", " +
1424 ConstructHeaderGroupString(
1425 MakeEndpointGroup(kGroup2_, endpoints2)); // Other group stays.
1426 ParseHeader(kNak_, kOrigin1_, header2);
1427
1428 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1429 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1430
1431 // Group was deleted.
1432 EXPECT_FALSE(
1433 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1434 // Other group remains in the cache.
1435 EXPECT_TRUE(
1436 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1437 EXPECT_EQ(1u, cache()->GetEndpointCount());
1438 if (mock_store()) {
1439 mock_store()->Flush();
1440 EXPECT_EQ(2,
1441 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1442 EXPECT_EQ(2, mock_store()->CountCommands(
1443 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1444 EXPECT_EQ(
1445 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1446 EXPECT_EQ(1, mock_store()->CountCommands(
1447 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1448 MockPersistentReportingStore::CommandList expected_commands;
1449 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1450 kGroupKey11_, kEndpoint1_);
1451 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1452 kGroupKey11_);
1453 EXPECT_THAT(mock_store()->GetAllCommands(),
1454 testing::IsSupersetOf(expected_commands));
1455 }
1456
1457 // Set another header with max_age: 0 to delete the other group. (Should work
1458 // even if the endpoints field is an empty list.)
1459 std::string header3 = ConstructHeaderGroupString(MakeEndpointGroup(
1460 kGroup2_, std::vector<ReportingEndpoint::EndpointInfo>(),
1461 OriginSubdomains::DEFAULT, base::Seconds(0)));
1462 ParseHeader(kNak_, kOrigin1_, header3);
1463
1464 // Deletion of the last remaining group also deletes the client for this
1465 // origin.
1466 EXPECT_FALSE(ClientExistsInCacheForOrigin(kOrigin1_));
1467 EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1468 EXPECT_EQ(0u, cache()->GetEndpointCount());
1469 if (mock_store()) {
1470 mock_store()->Flush();
1471 EXPECT_EQ(2,
1472 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1473 EXPECT_EQ(2, mock_store()->CountCommands(
1474 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1475 EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1476 CommandType::DELETE_REPORTING_ENDPOINT));
1477 EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1478 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1479 MockPersistentReportingStore::CommandList expected_commands;
1480 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1481 kGroupKey12_, kEndpoint2_);
1482 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1483 kGroupKey12_);
1484 EXPECT_THAT(mock_store()->GetAllCommands(),
1485 testing::IsSupersetOf(expected_commands));
1486 }
1487 }
1488
1489 // Invalid advertisements that parse as JSON should remove an endpoint group,
1490 // while those that don't are ignored.
TEST_P(ReportingHeaderParserTest,InvalidAdvertisementRemovesEndpointGroup)1491 TEST_P(ReportingHeaderParserTest, InvalidAdvertisementRemovesEndpointGroup) {
1492 std::string invalid_non_json_header = "Goats should wear hats.";
1493 std::string invalid_json_header = "\"Goats should wear hats.\"";
1494
1495 // Without a pre-existing client, neither invalid header does anything.
1496
1497 ASSERT_EQ(0u, cache()->GetEndpointCount());
1498 ParseHeader(kNak_, kOrigin1_, invalid_non_json_header);
1499 EXPECT_EQ(0u, cache()->GetEndpointCount());
1500 if (mock_store()) {
1501 mock_store()->Flush();
1502 EXPECT_EQ(0,
1503 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1504 EXPECT_EQ(0, mock_store()->CountCommands(
1505 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1506 }
1507
1508 ASSERT_EQ(0u, cache()->GetEndpointCount());
1509 ParseHeader(kNak_, kOrigin1_, invalid_json_header);
1510 EXPECT_EQ(0u, cache()->GetEndpointCount());
1511 if (mock_store()) {
1512 mock_store()->Flush();
1513 EXPECT_EQ(0,
1514 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1515 EXPECT_EQ(0, mock_store()->CountCommands(
1516 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1517 }
1518
1519 // Set a header with two endpoint groups.
1520 std::vector<ReportingEndpoint::EndpointInfo> endpoints1 = {{kEndpoint1_}};
1521 std::vector<ReportingEndpoint::EndpointInfo> endpoints2 = {{kEndpoint2_}};
1522 std::string header1 =
1523 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints1)) +
1524 ", " +
1525 ConstructHeaderGroupString(MakeEndpointGroup(kGroup2_, endpoints2));
1526 ParseHeader(kNak_, kOrigin1_, header1);
1527
1528 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1529 EXPECT_EQ(2u, cache()->GetEndpointGroupCountForTesting());
1530 EXPECT_TRUE(
1531 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1532 EXPECT_TRUE(
1533 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1534 EXPECT_EQ(2u, cache()->GetEndpointCount());
1535 if (mock_store()) {
1536 mock_store()->Flush();
1537 EXPECT_EQ(2,
1538 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1539 EXPECT_EQ(2, mock_store()->CountCommands(
1540 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1541 MockPersistentReportingStore::CommandList expected_commands;
1542 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1543 kGroupKey11_, kEndpoint1_);
1544 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT,
1545 kGroupKey12_, kEndpoint2_);
1546 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1547 kGroupKey11_);
1548 expected_commands.emplace_back(CommandType::ADD_REPORTING_ENDPOINT_GROUP,
1549 kGroupKey12_);
1550 EXPECT_THAT(mock_store()->GetAllCommands(),
1551 testing::IsSupersetOf(expected_commands));
1552 }
1553
1554 // Set another header with max_age: 0 to delete one of the groups.
1555 std::string header2 =
1556 ConstructHeaderGroupString(MakeEndpointGroup(
1557 kGroup1_, endpoints1, OriginSubdomains::DEFAULT, base::Seconds(0))) +
1558 ", " +
1559 ConstructHeaderGroupString(
1560 MakeEndpointGroup(kGroup2_, endpoints2)); // Other group stays.
1561 ParseHeader(kNak_, kOrigin1_, header2);
1562
1563 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1564 EXPECT_EQ(1u, cache()->GetEndpointGroupCountForTesting());
1565
1566 // Group was deleted.
1567 EXPECT_FALSE(
1568 EndpointGroupExistsInCache(kGroupKey11_, OriginSubdomains::DEFAULT));
1569 // Other group remains in the cache.
1570 EXPECT_TRUE(
1571 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1572 EXPECT_EQ(1u, cache()->GetEndpointCount());
1573 if (mock_store()) {
1574 mock_store()->Flush();
1575 EXPECT_EQ(2,
1576 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1577 EXPECT_EQ(2, mock_store()->CountCommands(
1578 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1579 EXPECT_EQ(
1580 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1581 EXPECT_EQ(1, mock_store()->CountCommands(
1582 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1583 MockPersistentReportingStore::CommandList expected_commands;
1584 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1585 kGroupKey11_, kEndpoint1_);
1586 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1587 kGroupKey11_);
1588 EXPECT_THAT(mock_store()->GetAllCommands(),
1589 testing::IsSupersetOf(expected_commands));
1590 }
1591
1592 // Invalid header values that are not JSON lists (without the outer brackets)
1593 // are ignored.
1594 ParseHeader(kNak_, kOrigin1_, invalid_non_json_header);
1595 EXPECT_TRUE(ClientExistsInCacheForOrigin(kOrigin1_));
1596 EXPECT_TRUE(
1597 EndpointGroupExistsInCache(kGroupKey12_, OriginSubdomains::DEFAULT));
1598 EXPECT_EQ(1u, cache()->GetEndpointCount());
1599 if (mock_store()) {
1600 mock_store()->Flush();
1601 EXPECT_EQ(2,
1602 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1603 EXPECT_EQ(2, mock_store()->CountCommands(
1604 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1605 EXPECT_EQ(
1606 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1607 EXPECT_EQ(1, mock_store()->CountCommands(
1608 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1609 MockPersistentReportingStore::CommandList expected_commands;
1610 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1611 kGroupKey11_, kEndpoint1_);
1612 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1613 kGroupKey11_);
1614 EXPECT_THAT(mock_store()->GetAllCommands(),
1615 testing::IsSupersetOf(expected_commands));
1616 }
1617
1618 // Invalid headers that do parse as JSON should delete the corresponding
1619 // client.
1620 ParseHeader(kNak_, kOrigin1_, invalid_json_header);
1621
1622 // Deletion of the last remaining group also deletes the client for this
1623 // origin.
1624 EXPECT_FALSE(ClientExistsInCacheForOrigin(kOrigin1_));
1625 EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1626 EXPECT_EQ(0u, cache()->GetEndpointCount());
1627 if (mock_store()) {
1628 mock_store()->Flush();
1629 EXPECT_EQ(2,
1630 mock_store()->CountCommands(CommandType::ADD_REPORTING_ENDPOINT));
1631 EXPECT_EQ(2, mock_store()->CountCommands(
1632 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1633 EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1634 CommandType::DELETE_REPORTING_ENDPOINT));
1635 EXPECT_EQ(1 + 1, mock_store()->CountCommands(
1636 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1637 MockPersistentReportingStore::CommandList expected_commands;
1638 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT,
1639 kGroupKey12_, kEndpoint2_);
1640 expected_commands.emplace_back(CommandType::DELETE_REPORTING_ENDPOINT_GROUP,
1641 kGroupKey12_);
1642 EXPECT_THAT(mock_store()->GetAllCommands(),
1643 testing::IsSupersetOf(expected_commands));
1644 }
1645 }
1646
TEST_P(ReportingHeaderParserTest,EvictEndpointsOverPerOriginLimit1)1647 TEST_P(ReportingHeaderParserTest, EvictEndpointsOverPerOriginLimit1) {
1648 // Set a header with too many endpoints, all in the same group.
1649 std::vector<ReportingEndpoint::EndpointInfo> endpoints;
1650 for (size_t i = 0; i < policy().max_endpoints_per_origin + 1; ++i) {
1651 endpoints.push_back({MakeURL(i)});
1652 }
1653 std::string header =
1654 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1655 ParseHeader(kNak_, kOrigin1_, header);
1656
1657 // Endpoint count should be at most the limit.
1658 EXPECT_GE(policy().max_endpoints_per_origin, cache()->GetEndpointCount());
1659
1660 if (mock_store()) {
1661 mock_store()->Flush();
1662 EXPECT_EQ(policy().max_endpoints_per_origin + 1,
1663 static_cast<unsigned long>(mock_store()->CountCommands(
1664 CommandType::ADD_REPORTING_ENDPOINT)));
1665 EXPECT_EQ(1, mock_store()->CountCommands(
1666 CommandType::ADD_REPORTING_ENDPOINT_GROUP));
1667 EXPECT_EQ(
1668 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1669 }
1670 }
1671
TEST_P(ReportingHeaderParserTest,EvictEndpointsOverPerOriginLimit2)1672 TEST_P(ReportingHeaderParserTest, EvictEndpointsOverPerOriginLimit2) {
1673 // Set a header with too many endpoints, in different groups.
1674 std::string header;
1675 for (size_t i = 0; i < policy().max_endpoints_per_origin + 1; ++i) {
1676 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{MakeURL(i)}};
1677 header = header + ConstructHeaderGroupString(MakeEndpointGroup(
1678 base::NumberToString(i), endpoints));
1679 if (i != policy().max_endpoints_per_origin)
1680 header = header + ", ";
1681 }
1682 ParseHeader(kNak_, kOrigin1_, header);
1683
1684 // Endpoint count should be at most the limit.
1685 EXPECT_GE(policy().max_endpoints_per_origin, cache()->GetEndpointCount());
1686
1687 if (mock_store()) {
1688 mock_store()->Flush();
1689 EXPECT_EQ(policy().max_endpoints_per_origin + 1,
1690 static_cast<unsigned long>(mock_store()->CountCommands(
1691 CommandType::ADD_REPORTING_ENDPOINT)));
1692 EXPECT_EQ(policy().max_endpoints_per_origin + 1,
1693 static_cast<unsigned long>(mock_store()->CountCommands(
1694 CommandType::ADD_REPORTING_ENDPOINT_GROUP)));
1695 EXPECT_EQ(
1696 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1697 EXPECT_EQ(1, mock_store()->CountCommands(
1698 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1699 }
1700 }
1701
TEST_P(ReportingHeaderParserTest,EvictEndpointsOverGlobalLimit)1702 TEST_P(ReportingHeaderParserTest, EvictEndpointsOverGlobalLimit) {
1703 // Set headers from different origins up to the global limit.
1704 for (size_t i = 0; i < policy().max_endpoint_count; ++i) {
1705 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{MakeURL(i)}};
1706 std::string header =
1707 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1708 ParseHeader(kNak_, url::Origin::Create(MakeURL(i)), header);
1709 }
1710 EXPECT_EQ(policy().max_endpoint_count, cache()->GetEndpointCount());
1711
1712 // Parse one more header to trigger eviction.
1713 ParseHeader(kNak_, kOrigin1_,
1714 "{\"endpoints\":[{\"url\":\"" + kEndpoint1_.spec() +
1715 "\"}],\"max_age\":1}");
1716
1717 // Endpoint count should be at most the limit.
1718 EXPECT_GE(policy().max_endpoint_count, cache()->GetEndpointCount());
1719
1720 if (mock_store()) {
1721 mock_store()->Flush();
1722 EXPECT_EQ(policy().max_endpoint_count + 1,
1723 static_cast<unsigned long>(mock_store()->CountCommands(
1724 CommandType::ADD_REPORTING_ENDPOINT)));
1725 EXPECT_EQ(policy().max_endpoint_count + 1,
1726 static_cast<unsigned long>(mock_store()->CountCommands(
1727 CommandType::ADD_REPORTING_ENDPOINT_GROUP)));
1728 EXPECT_EQ(
1729 1, mock_store()->CountCommands(CommandType::DELETE_REPORTING_ENDPOINT));
1730 EXPECT_EQ(1, mock_store()->CountCommands(
1731 CommandType::DELETE_REPORTING_ENDPOINT_GROUP));
1732 }
1733 }
1734
1735 INSTANTIATE_TEST_SUITE_P(ReportingHeaderParserStoreTest,
1736 ReportingHeaderParserTest,
1737 testing::Bool());
1738
1739 // This test is parametrized on a boolean that represents whether to use a
1740 // MockPersistentReportingStore.
1741 class ReportingHeaderParserStructuredHeaderTest
1742 : public ReportingHeaderParserTestBase {
1743 protected:
ReportingHeaderParserStructuredHeaderTest()1744 ReportingHeaderParserStructuredHeaderTest() {
1745 // Enable kDocumentReporting to support new StructuredHeader-based
1746 // Reporting-Endpoints header.
1747 feature_list_.InitWithFeatures(
1748 {features::kPartitionNelAndReportingByNetworkIsolationKey,
1749 features::kDocumentReporting},
1750 {});
1751 }
1752
1753 ~ReportingHeaderParserStructuredHeaderTest() override = default;
1754
MakeEndpointGroup(const std::string & name,const std::vector<ReportingEndpoint::EndpointInfo> & endpoints,url::Origin origin=url::Origin ())1755 ReportingEndpointGroup MakeEndpointGroup(
1756 const std::string& name,
1757 const std::vector<ReportingEndpoint::EndpointInfo>& endpoints,
1758 url::Origin origin = url::Origin()) {
1759 ReportingEndpointGroupKey group_key(kNak_ /* unused */,
1760 url::Origin() /* unused */, name);
1761 ReportingEndpointGroup group;
1762 group.group_key = group_key;
1763 group.include_subdomains = OriginSubdomains::EXCLUDE;
1764 group.ttl = base::Days(30);
1765 group.endpoints = std::move(endpoints);
1766 return group;
1767 }
1768
1769 // Constructs a string which would represent a single endpoint in a
1770 // Reporting-Endpoints header.
ConstructHeaderGroupString(const ReportingEndpointGroup & group)1771 std::string ConstructHeaderGroupString(const ReportingEndpointGroup& group) {
1772 std::string header = group.group_key.group_name;
1773 if (header.empty())
1774 return header;
1775 base::StrAppend(&header, {"="});
1776 if (group.endpoints.empty())
1777 return header;
1778 base::StrAppend(&header, {"\"", group.endpoints.front().url.spec(), "\""});
1779 return header;
1780 }
1781
ParseHeader(const base::UnguessableToken & reporting_source,const IsolationInfo & isolation_info,const url::Origin & origin,const std::string & header_string)1782 void ParseHeader(const base::UnguessableToken& reporting_source,
1783 const IsolationInfo& isolation_info,
1784 const url::Origin& origin,
1785 const std::string& header_string) {
1786 std::optional<base::flat_map<std::string, std::string>> header_map =
1787 ParseReportingEndpoints(header_string);
1788
1789 if (header_map) {
1790 ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
1791 context(), reporting_source, isolation_info,
1792 isolation_info.network_anonymization_key(), origin, *header_map);
1793 }
1794 }
ProcessParsedHeader(const base::UnguessableToken & reporting_source,const IsolationInfo & isolation_info,const url::Origin & origin,const std::optional<base::flat_map<std::string,std::string>> & header_map)1795 void ProcessParsedHeader(
1796 const base::UnguessableToken& reporting_source,
1797 const IsolationInfo& isolation_info,
1798 const url::Origin& origin,
1799 const std::optional<base::flat_map<std::string, std::string>>&
1800 header_map) {
1801 ReportingHeaderParser::ProcessParsedReportingEndpointsHeader(
1802 context(), reporting_source, isolation_info,
1803 isolation_info.network_anonymization_key(), origin, *header_map);
1804 }
1805
1806 const base::UnguessableToken kReportingSource_ =
1807 base::UnguessableToken::Create();
1808 };
1809
TEST_P(ReportingHeaderParserStructuredHeaderTest,ParseInvalid)1810 TEST_P(ReportingHeaderParserStructuredHeaderTest, ParseInvalid) {
1811 static const struct {
1812 const char* header_value;
1813 const char* description;
1814 } kInvalidHeaderTestCases[] = {
1815 {"default=", "missing url"},
1816 {"default=1", "non-string url"},
1817 };
1818
1819 for (auto& test_case : kInvalidHeaderTestCases) {
1820 auto parsed_result = ParseReportingEndpoints(test_case.header_value);
1821
1822 EXPECT_FALSE(parsed_result.has_value())
1823 << "Invalid Reporting-Endpoints header (" << test_case.description
1824 << ": \"" << test_case.header_value << "\") parsed as valid.";
1825 }
1826 }
1827
TEST_P(ReportingHeaderParserStructuredHeaderTest,ProcessInvalid)1828 TEST_P(ReportingHeaderParserStructuredHeaderTest, ProcessInvalid) {
1829 static const struct {
1830 const char* header_value;
1831 const char* description;
1832 } kInvalidHeaderTestCases[] = {
1833 {"default=\"//scheme/relative\"", "scheme-relative url"},
1834 {"default=\"relative/path\"", "path relative url"},
1835 {"default=\"http://insecure/\"", "insecure url"}};
1836
1837 base::HistogramTester histograms;
1838 int invalid_case_count = 0;
1839
1840 for (auto& test_case : kInvalidHeaderTestCases) {
1841 auto parsed_result = ParseReportingEndpoints(test_case.header_value);
1842
1843 EXPECT_TRUE(parsed_result.has_value())
1844 << "Syntactically valid Reporting-Endpoints header (\""
1845 << test_case.description << ": \"" << test_case.header_value
1846 << "\") parsed as invalid.";
1847 ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
1848 parsed_result);
1849
1850 invalid_case_count++;
1851 histograms.ExpectBucketCount(
1852 kReportingHeaderTypeHistogram,
1853 ReportingHeaderParser::ReportingHeaderType::kReportingEndpointsInvalid,
1854 invalid_case_count);
1855
1856 // The endpoint should not have been set up in the cache.
1857 ReportingEndpoint endpoint =
1858 cache()->GetV1EndpointForTesting(kReportingSource_, "default");
1859 EXPECT_FALSE(endpoint);
1860 }
1861 histograms.ExpectBucketCount(
1862 kReportingHeaderTypeHistogram,
1863 ReportingHeaderParser::ReportingHeaderType::kReportingEndpoints, 0);
1864 }
1865
TEST_P(ReportingHeaderParserStructuredHeaderTest,ParseBasic)1866 TEST_P(ReportingHeaderParserStructuredHeaderTest, ParseBasic) {
1867 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
1868
1869 std::string header =
1870 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1871 auto parsed_result = ParseReportingEndpoints(header);
1872
1873 EXPECT_TRUE(parsed_result.has_value())
1874 << "Valid Reporting-Endpoints header (\"" << header
1875 << "\") parsed as invalid.";
1876 EXPECT_EQ(1u, parsed_result->size());
1877 EXPECT_EQ(parsed_result->at(kGroup1_), kEndpoint1_.spec());
1878 }
1879
TEST_P(ReportingHeaderParserStructuredHeaderTest,Basic)1880 TEST_P(ReportingHeaderParserStructuredHeaderTest, Basic) {
1881 base::HistogramTester histograms;
1882 std::vector<ReportingEndpoint::EndpointInfo> endpoints = {{kEndpoint1_}};
1883
1884 std::string header =
1885 ConstructHeaderGroupString(MakeEndpointGroup(kGroup1_, endpoints));
1886 auto parsed_result = ParseReportingEndpoints(header);
1887 ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
1888 parsed_result);
1889
1890 // Ensure that the endpoint was not inserted into the persistent endpoint
1891 // groups used for v0 reporting.
1892 EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1893
1894 ReportingEndpoint endpoint =
1895 cache()->GetV1EndpointForTesting(kReportingSource_, kGroup1_);
1896 EXPECT_TRUE(endpoint);
1897
1898 IsolationInfo isolation_info = cache()->GetIsolationInfoForEndpoint(endpoint);
1899 EXPECT_TRUE(isolation_info.IsEqualForTesting(kIsolationInfo_));
1900 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
1901 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
1902 EXPECT_EQ(kEndpoint1_, endpoint.info.url);
1903 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1904 endpoint.info.priority);
1905 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1906 endpoint.info.weight);
1907 histograms.ExpectBucketCount(
1908 kReportingHeaderTypeHistogram,
1909 ReportingHeaderParser::ReportingHeaderType::kReportingEndpoints, 1);
1910
1911 // Ephemeral endpoints should not be persisted in the store
1912 if (mock_store()) {
1913 mock_store()->Flush();
1914 EXPECT_EQ(0, mock_store()->StoredEndpointsCount());
1915 EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount());
1916 }
1917 }
1918
TEST_P(ReportingHeaderParserStructuredHeaderTest,PathAbsoluteURLEndpoint)1919 TEST_P(ReportingHeaderParserStructuredHeaderTest, PathAbsoluteURLEndpoint) {
1920 base::HistogramTester histograms;
1921 std::string header = "group1=\"/path-absolute-url\"";
1922 auto parsed_result = ParseReportingEndpoints(header);
1923 ProcessParsedHeader(kReportingSource_, kIsolationInfo_, kOrigin1_,
1924 parsed_result);
1925
1926 // Ensure that the endpoint was not inserted into the persistent endpoint
1927 // groups used for v0 reporting.
1928 EXPECT_EQ(0u, cache()->GetEndpointGroupCountForTesting());
1929
1930 ReportingEndpoint endpoint =
1931 cache()->GetV1EndpointForTesting(kReportingSource_, kGroup1_);
1932 EXPECT_TRUE(endpoint);
1933 EXPECT_EQ(kOrigin1_, endpoint.group_key.origin);
1934 EXPECT_EQ(kGroup1_, endpoint.group_key.group_name);
1935 EXPECT_EQ(kEndpointPathAbsolute_, endpoint.info.url);
1936 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultPriority,
1937 endpoint.info.priority);
1938 EXPECT_EQ(ReportingEndpoint::EndpointInfo::kDefaultWeight,
1939 endpoint.info.weight);
1940 histograms.ExpectBucketCount(
1941 kReportingHeaderTypeHistogram,
1942 ReportingHeaderParser::ReportingHeaderType::kReportingEndpoints, 1);
1943
1944 // Ephemeral endpoints should not be persisted in the store
1945 if (mock_store()) {
1946 mock_store()->Flush();
1947 EXPECT_EQ(0, mock_store()->StoredEndpointsCount());
1948 EXPECT_EQ(0, mock_store()->StoredEndpointGroupsCount());
1949 }
1950 }
1951
1952 INSTANTIATE_TEST_SUITE_P(ReportingHeaderParserStoreTest,
1953 ReportingHeaderParserStructuredHeaderTest,
1954 testing::Bool());
1955
1956 } // namespace
1957 } // namespace net
1958