1 // Copyright 2020 The Chromium Authors. All rights reserved.
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 "chrome/browser/browsing_data/access_context_audit_service.h"
6
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/i18n/time_formatting.h"
9 #include "base/test/bind.h"
10 #include "base/test/scoped_feature_list.h"
11 #include "base/test/simple_test_clock.h"
12 #include "base/test/test_simple_task_runner.h"
13 #include "chrome/browser/browsing_data/access_context_audit_database.h"
14 #include "chrome/browser/browsing_data/access_context_audit_service_factory.h"
15 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
16 #include "chrome/browser/history/history_service_factory.h"
17 #include "chrome/common/chrome_features.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/browsing_data/content/local_shared_objects_container.h"
20 #include "components/content_settings/core/browser/host_content_settings_map.h"
21 #include "components/content_settings/core/common/content_settings_types.h"
22 #include "components/history/core/browser/history_database_params.h"
23 #include "components/history/core/browser/url_row.h"
24 #include "components/history/core/test/test_history_database.h"
25 #include "content/public/browser/cookie_access_details.h"
26 #include "content/public/browser/storage_partition.h"
27 #include "content/public/test/browser_task_environment.h"
28 #include "content/public/test/test_storage_partition.h"
29 #include "services/network/test/test_cookie_manager.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace {
33
34 // Checks that a record exists in |records| that matches both |cookie| and
35 // |top_frame_origin|.
CheckContainsCookieRecord(net::CanonicalCookie * cookie,url::Origin top_frame_origin,base::Time last_access_time,const std::vector<AccessContextAuditDatabase::AccessRecord> & records)36 void CheckContainsCookieRecord(
37 net::CanonicalCookie* cookie,
38 url::Origin top_frame_origin,
39 base::Time last_access_time,
40 const std::vector<AccessContextAuditDatabase::AccessRecord>& records) {
41 EXPECT_NE(
42 std::find_if(
43 records.begin(), records.end(),
44 [=](const AccessContextAuditDatabase::AccessRecord& record) {
45 return record.type ==
46 AccessContextAuditDatabase::StorageAPIType::kCookie &&
47 record.top_frame_origin == top_frame_origin &&
48 record.name == cookie->Name() &&
49 record.domain == cookie->Domain() &&
50 record.path == cookie->Path() &&
51 record.last_access_time == last_access_time &&
52 record.is_persistent == cookie->IsPersistent();
53 }),
54 records.end());
55 }
56
57 // Checks that info in |record| matches storage API access defined by
58 // |storage_origin|, |type| and |top_frame_origin|
CheckContainsStorageAPIRecord(url::Origin storage_origin,AccessContextAuditDatabase::StorageAPIType type,url::Origin top_frame_origin,base::Time last_access_time,const std::vector<AccessContextAuditDatabase::AccessRecord> & records)59 void CheckContainsStorageAPIRecord(
60 url::Origin storage_origin,
61 AccessContextAuditDatabase::StorageAPIType type,
62 url::Origin top_frame_origin,
63 base::Time last_access_time,
64 const std::vector<AccessContextAuditDatabase::AccessRecord>& records) {
65 EXPECT_NE(
66 std::find_if(records.begin(), records.end(),
67 [=](const AccessContextAuditDatabase::AccessRecord& record) {
68 return record.type == type &&
69 record.origin == storage_origin &&
70 record.top_frame_origin == top_frame_origin &&
71 record.last_access_time == last_access_time;
72 }),
73 records.end());
74 }
75
76 } // namespace
77
78 class TestCookieManager : public network::TestCookieManager {
79 public:
AddGlobalChangeListener(mojo::PendingRemote<network::mojom::CookieChangeListener> notification_pointer)80 void AddGlobalChangeListener(
81 mojo::PendingRemote<network::mojom::CookieChangeListener>
82 notification_pointer) override {
83 listener_registered_ = true;
84 }
85
ListenerRegistered()86 bool ListenerRegistered() { return listener_registered_; }
87
88 protected:
89 bool listener_registered_ = false;
90 };
91
92 class AccessContextAuditServiceTest : public testing::Test {
93 public:
94 AccessContextAuditServiceTest() = default;
95
BuildTestHistoryService(content::BrowserContext * context)96 std::unique_ptr<KeyedService> BuildTestHistoryService(
97 content::BrowserContext* context) {
98 std::unique_ptr<history::HistoryService> service(
99 std::make_unique<history::HistoryService>());
100 service->Init(
101 history::TestHistoryDatabaseParamsForPath(temp_directory_.GetPath()));
102 // Store a pointer to the service before passing ownership, as it is needed
103 // during creation of the test context audit service. Its location cannot be
104 // derived from the testing profile as it is not built at that point.
105 history_service_ = service.get();
106 return service;
107 }
108
BuildTestContextAuditService(content::BrowserContext * context)109 std::unique_ptr<KeyedService> BuildTestContextAuditService(
110 content::BrowserContext* context) {
111 std::unique_ptr<AccessContextAuditService> service(
112 new AccessContextAuditService(static_cast<Profile*>(context)));
113 service->SetTaskRunnerForTesting(task_runner_);
114 service->Init(temp_directory_.GetPath(), cookie_manager(),
115 history_service(), storage_partition());
116 return service;
117 }
118
SetUp()119 void SetUp() override {
120 feature_list_.InitWithFeatures(
121 {features::kClientStorageAccessContextAuditing}, {});
122
123 task_runner_ = base::ThreadPool::CreateUpdateableSequencedTaskRunner(
124 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
125 base::ThreadPolicy::PREFER_BACKGROUND,
126 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
127
128 ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
129
130 TestingProfile::Builder builder;
131 builder.AddTestingFactory(
132 AccessContextAuditServiceFactory::GetInstance(),
133 base::BindRepeating(
134 &AccessContextAuditServiceTest::BuildTestContextAuditService,
135 base::Unretained(this)));
136 builder.AddTestingFactory(
137 HistoryServiceFactory::GetInstance(),
138 base::BindRepeating(
139 &AccessContextAuditServiceTest::BuildTestHistoryService,
140 base::Unretained(this)));
141 builder.SetPath(temp_directory_.GetPath());
142 profile_ = builder.Build();
143 FlushSequencedTaskRunner();
144 browser_task_environment_.RunUntilIdle();
145 }
146
GetAllAccessRecords()147 std::vector<AccessContextAuditDatabase::AccessRecord> GetAllAccessRecords() {
148 base::RunLoop run_loop;
149 std::vector<AccessContextAuditDatabase::AccessRecord> records_out;
150 service()->GetAllAccessRecords(base::BindLambdaForTesting(
151 [&](std::vector<AccessContextAuditDatabase::AccessRecord> records) {
152 records_out = records;
153 run_loop.QuitWhenIdle();
154 }));
155 run_loop.Run();
156 return records_out;
157 }
158
FlushSequencedTaskRunner()159 void FlushSequencedTaskRunner() {
160 base::RunLoop run_loop;
161 task_runner_->PostTask(FROM_HERE, base::BindLambdaForTesting(
162 [&]() { run_loop.QuitWhenIdle(); }));
163 run_loop.Run();
164 }
165
cookie_manager()166 TestCookieManager* cookie_manager() { return &cookie_manager_; }
storage_partition()167 content::TestStoragePartition* storage_partition() {
168 return &storage_partition_;
169 }
clock()170 base::SimpleTestClock* clock() { return &clock_; }
profile()171 TestingProfile* profile() { return profile_.get(); }
history_service()172 history::HistoryService* history_service() { return history_service_; }
service()173 AccessContextAuditService* service() {
174 return AccessContextAuditServiceFactory::GetForProfile(profile());
175 }
176
177 protected:
178 content::BrowserTaskEnvironment browser_task_environment_;
179 std::unique_ptr<TestingProfile> profile_;
180 base::SimpleTestClock clock_;
181 base::ScopedTempDir temp_directory_;
182 TestCookieManager cookie_manager_;
183 content::TestStoragePartition storage_partition_;
184 history::HistoryService* history_service_;
185 base::test::ScopedFeatureList feature_list_;
186
187 scoped_refptr<base::UpdateableSequencedTaskRunner> task_runner_;
188 std::vector<AccessContextAuditDatabase::AccessRecord> records_;
189 };
190
TEST_F(AccessContextAuditServiceTest,RegisterDeletionObservers)191 TEST_F(AccessContextAuditServiceTest, RegisterDeletionObservers) {
192 // Check that the service correctly registers observers for deletion.
193 EXPECT_TRUE(cookie_manager_.ListenerRegistered());
194 EXPECT_EQ(1, storage_partition()->GetDataRemovalObserverCount());
195 }
196
TEST_F(AccessContextAuditServiceTest,CookieRecords)197 TEST_F(AccessContextAuditServiceTest, CookieRecords) {
198 // Check that cookie access records are successfully stored and deleted.
199 GURL kTestCookieURL("https://example.com");
200 std::string kTestCookieName = "test";
201 std::string kTestNonPersistentCookieName = "test-non-persistent";
202 const base::Time kAccessTime1 = base::Time::Now();
203 clock()->SetNow(kAccessTime1);
204 service()->SetClockForTesting(clock());
205
206 auto test_cookie = net::CanonicalCookie::Create(
207 kTestCookieURL, kTestCookieName + "=1; max-age=3600", kAccessTime1,
208 base::nullopt /* server_time */);
209 auto test_non_persistent_cookie = net::CanonicalCookie::Create(
210 kTestCookieURL, kTestNonPersistentCookieName + "=1", kAccessTime1,
211 base::nullopt /* server_time */);
212
213 // Record access to these cookies against a URL.
214 url::Origin kTopFrameOrigin = url::Origin::Create(GURL("https://test.com"));
215 service()->RecordCookieAccess({*test_cookie, *test_non_persistent_cookie},
216 kTopFrameOrigin);
217
218 // Ensure that the record of these accesses is correctly returned.
219 auto records = GetAllAccessRecords();
220 EXPECT_EQ(2u, records.size());
221 CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime1,
222 records);
223 CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
224 kAccessTime1, records);
225
226 // Check that informing the service of non-deletion changes to the cookies
227 // via the CookieChangeInterface is a no-op.
228 service()->OnCookieChange(
229 net::CookieChangeInfo(*test_cookie, net::CookieAccessResult(),
230 net::CookieChangeCause::OVERWRITE));
231 service()->OnCookieChange(net::CookieChangeInfo(
232 *test_non_persistent_cookie, net::CookieAccessResult(),
233 net::CookieChangeCause::OVERWRITE));
234
235 records = GetAllAccessRecords();
236 EXPECT_EQ(2u, records.size());
237 CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime1,
238 records);
239 CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
240 kAccessTime1, records);
241
242 // Check that a repeated access correctly updates associated timestamp.
243 clock()->Advance(base::TimeDelta::FromHours(1));
244 const base::Time kAccessTime2 = clock()->Now();
245 service()->RecordCookieAccess({*test_cookie, *test_non_persistent_cookie},
246 kTopFrameOrigin);
247
248 records = GetAllAccessRecords();
249 EXPECT_EQ(2u, records.size());
250 CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime2,
251 records);
252 CheckContainsCookieRecord(test_non_persistent_cookie.get(), kTopFrameOrigin,
253 kAccessTime2, records);
254
255 // Inform the service the cookies have been deleted and check they are no
256 // longer returned.
257 service()->OnCookieChange(
258 net::CookieChangeInfo(*test_cookie, net::CookieAccessResult(),
259 net::CookieChangeCause::EXPLICIT));
260 service()->OnCookieChange(net::CookieChangeInfo(
261 *test_non_persistent_cookie, net::CookieAccessResult(),
262 net::CookieChangeCause::EXPLICIT));
263 records = GetAllAccessRecords();
264 EXPECT_EQ(0u, records.size());
265 }
266
TEST_F(AccessContextAuditServiceTest,ExpiredCookies)267 TEST_F(AccessContextAuditServiceTest, ExpiredCookies) {
268 // Check that no accesses are recorded for cookies which have already expired.
269 const GURL kTestURL("https://test.com");
270 auto test_cookie_expired = net::CanonicalCookie::Create(
271 kTestURL, "test_1=1; expires=Thu, 01 Jan 1970 00:00:00 GMT",
272 base::Time::Now(), base::nullopt /* server_time */);
273
274 service()->RecordCookieAccess({*test_cookie_expired},
275 url::Origin::Create(kTestURL));
276
277 EXPECT_EQ(0u, GetAllAccessRecords().size());
278 }
279
TEST_F(AccessContextAuditServiceTest,OriginKeyedStorageDeleted)280 TEST_F(AccessContextAuditServiceTest, OriginKeyedStorageDeleted) {
281 // Check that informing the service that an origin's storage of a particular
282 // type as been deleted removes all records of that storage.
283 const auto kTestStorageType1 =
284 AccessContextAuditDatabase::StorageAPIType::kWebDatabase;
285 const auto kTestStorageType2 =
286 AccessContextAuditDatabase::StorageAPIType::kAppCache;
287 const url::Origin kTestOrigin1 =
288 url::Origin::Create(GURL("https://example.com"));
289 const url::Origin kTestOrigin2 =
290 url::Origin::Create(GURL("https://example2.com"));
291 const url::Origin kTestTopLevelOrigin =
292 url::Origin::Create(GURL("https://example3.com"));
293 const base::Time kAccessTime = base::Time::Now();
294 clock()->SetNow(kAccessTime);
295 service()->SetClockForTesting(clock());
296
297 // Record accesses for the 4 possible test type and origin combinations.
298 service()->RecordStorageAPIAccess(kTestOrigin1, kTestStorageType1,
299 kTestTopLevelOrigin);
300 service()->RecordStorageAPIAccess(kTestOrigin1, kTestStorageType2,
301 kTestTopLevelOrigin);
302 service()->RecordStorageAPIAccess(kTestOrigin2, kTestStorageType1,
303 kTestTopLevelOrigin);
304 service()->RecordStorageAPIAccess(kTestOrigin2, kTestStorageType2,
305 kTestTopLevelOrigin);
306
307 // Remove records for Origin1 and Type1 and ensure the record is removed, but
308 // those for Origin2 or Type2 are not.
309 service()->RemoveAllRecordsForOriginKeyedStorage(kTestOrigin1,
310 kTestStorageType1);
311
312 auto records = GetAllAccessRecords();
313 EXPECT_EQ(3u, records.size());
314 CheckContainsStorageAPIRecord(kTestOrigin1, kTestStorageType2,
315 kTestTopLevelOrigin, kAccessTime, records);
316 CheckContainsStorageAPIRecord(kTestOrigin2, kTestStorageType1,
317 kTestTopLevelOrigin, kAccessTime, records);
318 CheckContainsStorageAPIRecord(kTestOrigin2, kTestStorageType2,
319 kTestTopLevelOrigin, kAccessTime, records);
320 }
321
TEST_F(AccessContextAuditServiceTest,HistoryDeletion)322 TEST_F(AccessContextAuditServiceTest, HistoryDeletion) {
323 // Check when the last record of an origin is deleted from history all records
324 // with it as a top frame origin are also removed.
325 const auto kTestStorageType =
326 AccessContextAuditDatabase::StorageAPIType::kWebDatabase;
327 const url::Origin kTestStorageOrigin =
328 url::Origin::Create(GURL("http://test.com"));
329 const GURL kTestCookieURL("https://example.com");
330 const std::string kTestCookieName = "test";
331 const GURL kURL1 = GURL("https://remaining-entries.com/test1");
332 const GURL kURL2 = GURL("https://remaining-entries.com/test2");
333 const GURL kURL3 = GURL("https://no-remaining-entries.com/test1");
334 const url::Origin kHistoryEntriesRemainingOrigin = url::Origin::Create(kURL1);
335 const url::Origin kNoRemainingHistoryEntriesOrigin =
336 url::Origin::Create(kURL3);
337 const base::Time kAccessTime = base::Time::Now();
338 clock()->SetNow(kAccessTime);
339 service()->SetClockForTesting(clock());
340
341 auto test_cookie = net::CanonicalCookie::Create(
342 kTestCookieURL, kTestCookieName + "=1; max-age=3600", kAccessTime,
343 base::nullopt /* server_time */);
344
345 // Record access for two top level origins for the same storage and cookie.
346 service()->RecordCookieAccess({*test_cookie}, kHistoryEntriesRemainingOrigin);
347 service()->RecordCookieAccess({*test_cookie},
348 kNoRemainingHistoryEntriesOrigin);
349 service()->RecordStorageAPIAccess(kTestStorageOrigin, kTestStorageType,
350 kHistoryEntriesRemainingOrigin);
351 service()->RecordStorageAPIAccess(kTestStorageOrigin, kTestStorageType,
352 kNoRemainingHistoryEntriesOrigin);
353
354 // Ensure all records have been initially recorded.
355 EXPECT_EQ(4u, GetAllAccessRecords().size());
356
357 // Add history entries for all three URLs, then remove history entries for
358 // URL1 and URL3. This will fire a history deletion event where the shared
359 // origin of URL1 & URL2 has a remaining history entry, but no entry for the
360 // URL3 origin remains.
361 history_service()->AddPageWithDetails(kURL1, base::ASCIIToUTF16("Test 1"), 1,
362 1, base::Time::Now(), false,
363 history::SOURCE_BROWSED);
364 history_service()->AddPageWithDetails(kURL2, base::ASCIIToUTF16("Test 2"), 1,
365 1, base::Time::Now(), false,
366 history::SOURCE_BROWSED);
367 history_service()->AddPageWithDetails(kURL3, base::ASCIIToUTF16("Test 3"), 1,
368 1, base::Time::Now(), false,
369 history::SOURCE_BROWSED);
370 history_service()->DeleteURLs({kURL1, kURL3});
371 base::RunLoop run_loop;
372 history_service()->FlushForTest(run_loop.QuitClosure());
373 run_loop.Run();
374
375 // Confirm that the records for the origin of URL3 have been removed, but the
376 // records for the shared origin of URL1 & URL2 remain.
377 auto records = GetAllAccessRecords();
378 EXPECT_EQ(2u, records.size());
379 CheckContainsCookieRecord(test_cookie.get(), kHistoryEntriesRemainingOrigin,
380 kAccessTime, records);
381 CheckContainsStorageAPIRecord(kTestStorageOrigin, kTestStorageType,
382 kHistoryEntriesRemainingOrigin, kAccessTime,
383 records);
384 }
385
TEST_F(AccessContextAuditServiceTest,AllHistoryDeletion)386 TEST_F(AccessContextAuditServiceTest, AllHistoryDeletion) {
387 // Test that a deletion for all history removes all records, including those
388 // for origins without any history entries.
389 const GURL kHistoryEntryURL = GURL("https://history.com/test1");
390 const url::Origin kHistoryEntryOrigin = url::Origin::Create(kHistoryEntryURL);
391 const url::Origin kNoHistoryEntryOrigin =
392 url::Origin::Create(GURL("https://no-history-entry.com/"));
393 history_service()->AddPageWithDetails(
394 kHistoryEntryURL, base::ASCIIToUTF16("Test"), 1, 1, base::Time::Now(),
395 false, history::SOURCE_BROWSED);
396
397 // Record two sets of unrelated accesses to cookies and storage APIs, one for
398 // the origin with a history entry, and one for the origin without.
399 service()->RecordCookieAccess(
400 {*net::CanonicalCookie::Create(GURL("https://foo.com"),
401 "foo=1; max-age=3600", base::Time::Now(),
402 base::nullopt /* server_time */)},
403 kHistoryEntryOrigin);
404 service()->RecordCookieAccess(
405 {*net::CanonicalCookie::Create(GURL("https://bar.com"),
406 "bar=1; max-age=3600", base::Time::Now(),
407 base::nullopt /* server_time */)},
408 kNoHistoryEntryOrigin);
409 service()->RecordStorageAPIAccess(
410 url::Origin::Create(GURL("https://foo.com")),
411 AccessContextAuditDatabase::StorageAPIType::kWebDatabase,
412 kHistoryEntryOrigin);
413 service()->RecordStorageAPIAccess(
414 url::Origin::Create(GURL("https://bar.com")),
415 AccessContextAuditDatabase::StorageAPIType::kIndexedDB,
416 kNoHistoryEntryOrigin);
417
418 // Check access has been initially recorded.
419 EXPECT_EQ(4u, GetAllAccessRecords().size());
420
421 // Expire all history and confirm that all records are removed.
422 base::RunLoop run_loop;
423 base::CancelableTaskTracker task_tracker;
424 history_service()->ExpireHistoryBetween(
425 std::set<GURL>(), base::Time(), base::Time(),
426 /*user_initiated*/ true, run_loop.QuitClosure(), &task_tracker);
427 run_loop.Run();
428
429 EXPECT_EQ(0u, GetAllAccessRecords().size());
430 }
431
TEST_F(AccessContextAuditServiceTest,TimeRangeHistoryDeletion)432 TEST_F(AccessContextAuditServiceTest, TimeRangeHistoryDeletion) {
433 // Test that deleting a time range of history records correctly removes
434 // records within the time range, as well as records for which no history
435 // entry for the top frame origin remains.
436
437 // Create a situation where origin https://foo.com has history entries and
438 // access records with timestamps both inside and outside the deleted range.
439 // Additionally create a single history entry for origin https://bar.com
440 // inside the deleted range, and multiple access records outside the range.
441 // After deletion, the access records for https://foo.com outside the deletion
442 // range should still be present, while all access records https://bar.com
443 // should have been removed.
444
445 const GURL kURL1 = GURL("https://foo.com/example.html");
446 const GURL kURL2 = GURL("https://bar.com/another.html");
447 const url::Origin kOrigin1 = url::Origin::Create(kURL1);
448 const url::Origin kOrigin2 = url::Origin::Create(kURL2);
449 const GURL kTestCookieURL = GURL("https://test.com");
450 const auto kTestStorageType1 =
451 AccessContextAuditDatabase::StorageAPIType::kWebDatabase;
452 const auto kTestStorageType2 =
453 AccessContextAuditDatabase::StorageAPIType::kAppCache;
454
455 clock()->SetNow(base::Time::Now());
456 service()->SetClockForTesting(clock());
457 const base::Time kInsideTimeRange =
458 clock()->Now() + base::TimeDelta::FromHours(1);
459 const base::Time kOutsideTimeRange =
460 clock()->Now() + base::TimeDelta::FromHours(3);
461
462 history_service()->AddPageWithDetails(kURL1, base::ASCIIToUTF16("Test1"), 1,
463 1, kInsideTimeRange, false,
464 history::SOURCE_BROWSED);
465 history_service()->AddPageWithDetails(kURL2, base::ASCIIToUTF16("Test2"), 1,
466 1, kInsideTimeRange, false,
467 history::SOURCE_BROWSED);
468 history_service()->AddPageWithDetails(kURL1, base::ASCIIToUTF16("Test3"), 1,
469 1, kOutsideTimeRange, false,
470 history::SOURCE_BROWSED);
471
472 // Record accesses to cookies both inside and outside the deletion range.
473 auto cookie_accessed_in_range = net::CanonicalCookie::Create(
474 kTestCookieURL, "inside=1; max-age=3600", kInsideTimeRange,
475 base::nullopt /* server_time */);
476 auto cookie_accessed_outside_range = net::CanonicalCookie::Create(
477 kTestCookieURL, "outside=1; max-age=3600", kOutsideTimeRange,
478 base::nullopt /* server_time */);
479
480 clock()->SetNow(kInsideTimeRange);
481 service()->RecordCookieAccess({*cookie_accessed_in_range}, kOrigin1);
482 clock()->SetNow(kOutsideTimeRange);
483 service()->RecordCookieAccess({*cookie_accessed_outside_range}, kOrigin1);
484 service()->RecordCookieAccess({*cookie_accessed_outside_range}, kOrigin2);
485
486 // Record accesses to storage APIs both inside and outside the deletion range.
487 clock()->SetNow(kInsideTimeRange);
488 service()->RecordStorageAPIAccess(kOrigin1, kTestStorageType1, kOrigin1);
489 clock()->SetNow(kOutsideTimeRange);
490 service()->RecordStorageAPIAccess(kOrigin1, kTestStorageType2, kOrigin1);
491 service()->RecordStorageAPIAccess(kOrigin2, kTestStorageType1, kOrigin2);
492
493 // Ensure all records have been initially recorded.
494 EXPECT_EQ(6u, GetAllAccessRecords().size());
495
496 // Expire history in target time range.
497 base::RunLoop run_loop;
498 base::CancelableTaskTracker task_tracker;
499 history_service()->ExpireHistoryBetween(
500 std::set<GURL>(), kInsideTimeRange - base::TimeDelta::FromMinutes(10),
501 kInsideTimeRange + base::TimeDelta::FromMinutes(10),
502 /*user_initiated*/ true, run_loop.QuitClosure(), &task_tracker);
503 run_loop.Run();
504
505 // Ensure records have been removed as expected.
506 auto records = GetAllAccessRecords();
507 EXPECT_EQ(2u, records.size());
508 CheckContainsCookieRecord(cookie_accessed_outside_range.get(), kOrigin1,
509 kOutsideTimeRange, records);
510 CheckContainsStorageAPIRecord(kOrigin1, kTestStorageType2, kOrigin1,
511 kOutsideTimeRange, records);
512 }
513
TEST_F(AccessContextAuditServiceTest,SessionOnlyRecords)514 TEST_F(AccessContextAuditServiceTest, SessionOnlyRecords) {
515 // Check that data for cookie domains and storage origins are cleared on
516 // service shutdown when the associated content settings indicate they should.
517 const GURL kTestPersistentURL("https://persistent.com");
518 const GURL kTestSessionOnlyExplicitURL("https://explicit-session-only.com");
519 const GURL kTestSessionOnlyContentSettingURL("https://content-setting.com");
520 url::Origin kTopFrameOrigin = url::Origin::Create(GURL("https://test.com"));
521 std::string kTestCookieName = "test";
522 const auto kTestStorageType =
523 AccessContextAuditDatabase::StorageAPIType::kWebDatabase;
524 const base::Time kAccessTime = base::Time::Now();
525 clock()->SetNow(kAccessTime);
526 service()->SetClockForTesting(clock());
527
528 // Create a cookie that will persist after shutdown.
529 auto test_cookie_persistent = net::CanonicalCookie::Create(
530 kTestPersistentURL, kTestCookieName + "=1; max-age=3600", kAccessTime,
531 base::nullopt /* server_time */);
532
533 // Create a cookie that will persist (be cleared on next startup) because it
534 // is explicitly session only.
535 auto test_cookie_session_only_explicit = net::CanonicalCookie::Create(
536 kTestSessionOnlyExplicitURL, kTestCookieName + "=1", kAccessTime,
537 base::nullopt /* server_time */);
538
539 // Create a cookie that will be cleared because the content setting associated
540 // with the cookie domain is set to session only.
541 auto test_cookie_session_only_content_setting = net::CanonicalCookie::Create(
542 kTestSessionOnlyContentSettingURL, kTestCookieName + "=1; max-age=3600",
543 kAccessTime, base::nullopt /* server_time */);
544
545 service()->RecordCookieAccess(
546 {*test_cookie_persistent, *test_cookie_session_only_explicit,
547 *test_cookie_session_only_content_setting},
548 kTopFrameOrigin);
549
550 // Record storage APIs for both persistent and content setting based session
551 // only URLs.
552 service()->RecordStorageAPIAccess(url::Origin::Create(kTestPersistentURL),
553 kTestStorageType, kTopFrameOrigin);
554 service()->RecordStorageAPIAccess(
555 url::Origin::Create(kTestSessionOnlyContentSettingURL), kTestStorageType,
556 kTopFrameOrigin);
557
558 // Ensure all records have been initially recorded.
559 EXPECT_EQ(5u, GetAllAccessRecords().size());
560
561 // Apply Session Only exception.
562 HostContentSettingsMapFactory::GetForProfile(profile())
563 ->SetContentSettingDefaultScope(
564 kTestSessionOnlyContentSettingURL, GURL(),
565 ContentSettingsType::COOKIES,
566 ContentSetting::CONTENT_SETTING_SESSION_ONLY);
567
568 // Instruct service to clear session only records and check that they are
569 // correctly removed.
570 service()->ClearSessionOnlyRecords();
571
572 auto records = GetAllAccessRecords();
573 ASSERT_EQ(3u, records.size());
574 CheckContainsCookieRecord(test_cookie_persistent.get(), kTopFrameOrigin,
575 kAccessTime, records);
576 CheckContainsCookieRecord(test_cookie_session_only_explicit.get(),
577 kTopFrameOrigin, kAccessTime, records);
578 CheckContainsStorageAPIRecord(url::Origin::Create(GURL(kTestPersistentURL)),
579 kTestStorageType, kTopFrameOrigin, kAccessTime,
580 records);
581
582 // Update the default content setting to SESSION_ONLY and ensure that all
583 // records are cleared.
584 HostContentSettingsMapFactory::GetForProfile(profile())
585 ->SetDefaultContentSetting(ContentSettingsType::COOKIES,
586 ContentSetting::CONTENT_SETTING_SESSION_ONLY);
587 service()->ClearSessionOnlyRecords();
588 records = GetAllAccessRecords();
589 ASSERT_EQ(0u, records.size());
590 }
591
TEST_F(AccessContextAuditServiceTest,OnOriginDataCleared)592 TEST_F(AccessContextAuditServiceTest, OnOriginDataCleared) {
593 // Check that providing parameters with varying levels of specificity to the
594 // OnOriginDataCleared function all clear data correctly.
595 auto kTopFrameOrigin = url::Origin::Create(GURL("https://example.com"));
596 auto kTestOrigin1 = url::Origin::Create(GURL("https://test1.com"));
597 auto kTestOrigin2 = url::Origin::Create(GURL("https://test2.com"));
598 auto kTestOrigin3 = url::Origin::Create(GURL("https://test3.com"));
599
600 const auto kTestStorageType1 =
601 AccessContextAuditDatabase::StorageAPIType::kWebDatabase;
602 const auto kTestStorageType2 =
603 AccessContextAuditDatabase::StorageAPIType::kIndexedDB;
604 const auto kTestStorageType3 =
605 AccessContextAuditDatabase::StorageAPIType::kAppCache;
606
607 clock()->SetNow(base::Time());
608 service()->SetClockForTesting(clock());
609
610 clock()->Advance(base::TimeDelta::FromHours(1));
611 service()->RecordStorageAPIAccess(kTestOrigin1, kTestStorageType1,
612 kTopFrameOrigin);
613
614 clock()->Advance(base::TimeDelta::FromHours(1));
615 const base::Time kAccessTime1 = clock()->Now();
616 service()->RecordStorageAPIAccess(kTestOrigin2, kTestStorageType2,
617 kTopFrameOrigin);
618
619 clock()->Advance(base::TimeDelta::FromHours(1));
620 const base::Time kAccessTime2 = clock()->Now();
621 service()->RecordStorageAPIAccess(kTestOrigin3, kTestStorageType3,
622 kTopFrameOrigin);
623 EXPECT_EQ(3U, GetAllAccessRecords().size());
624
625 // Provide all parameters such that TestOrigin1's record is removed.
626 auto origin_matcher = base::BindLambdaForTesting(
627 [&](const url::Origin& origin) { return origin == kTestOrigin1; });
628 service()->OnOriginDataCleared(
629 content::StoragePartition::REMOVE_DATA_MASK_WEBSQL, origin_matcher,
630 base::Time() + base::TimeDelta::FromMinutes(50),
631 base::Time() + base::TimeDelta::FromMinutes(80));
632
633 auto records = GetAllAccessRecords();
634 ASSERT_EQ(2U, records.size());
635 CheckContainsStorageAPIRecord(kTestOrigin2, kTestStorageType2,
636 kTopFrameOrigin, kAccessTime1, records);
637 CheckContainsStorageAPIRecord(kTestOrigin3, kTestStorageType3,
638 kTopFrameOrigin, kAccessTime2, records);
639
640 // Provide more generalised parameters that target TestOrigin2's record.
641 service()->OnOriginDataCleared(
642 content::StoragePartition::REMOVE_DATA_MASK_ALL, base::NullCallback(),
643 base::Time() + base::TimeDelta::FromMinutes(80),
644 base::Time() + base::TimeDelta::FromMinutes(130));
645
646 records = GetAllAccessRecords();
647 ASSERT_EQ(1U, records.size());
648 CheckContainsStorageAPIRecord(kTestOrigin3, kTestStorageType3,
649 kTopFrameOrigin, kAccessTime2, records);
650
651 // Provide broadest possible parameters which should result in the final
652 // record being removed.
653 service()->OnOriginDataCleared(
654 content::StoragePartition::REMOVE_DATA_MASK_ALL, base::NullCallback(),
655 base::Time(), base::Time::Max());
656
657 records = GetAllAccessRecords();
658 ASSERT_EQ(0U, records.size());
659 }
660
TEST_F(AccessContextAuditServiceTest,OpaqueOrigins)661 TEST_F(AccessContextAuditServiceTest, OpaqueOrigins) {
662 // Check that records which have opaque top frame origins are not recorded.
663 auto test_cookie = net::CanonicalCookie::Create(
664 GURL("https://example.com"), "test_1=1; max-age=3600", base::Time::Now(),
665 base::nullopt /* server_time */);
666 service()->RecordCookieAccess({*test_cookie}, url::Origin());
667 service()->RecordStorageAPIAccess(
668 url::Origin::Create(GURL("https://example.com")),
669 AccessContextAuditDatabase::StorageAPIType::kWebDatabase, url::Origin());
670
671 auto records = GetAllAccessRecords();
672 ASSERT_EQ(0U, records.size());
673 }
674
TEST_F(AccessContextAuditServiceTest,CookieAccessHelper)675 TEST_F(AccessContextAuditServiceTest, CookieAccessHelper) {
676 // Check that the CookieAccessHelper is correctly forwarding accesses as
677 // appropriate and responding to deletions.
678 url::Origin kTopFrameOrigin = url::Origin::Create(GURL("https://test.com"));
679 GURL kTestCookieURL("https://example.com");
680 std::string kTestCookieName = "test";
681 const base::Time kAccessTime1 = base::Time::Now();
682 clock()->SetNow(kAccessTime1);
683 service()->SetClockForTesting(clock());
684
685 auto test_cookie = net::CanonicalCookie::Create(
686 kTestCookieURL, kTestCookieName + "=1; max-age=3600", kAccessTime1,
687 base::nullopt /* server_time */);
688
689 // Record access to the cookie via a helper.
690 auto helper = std::make_unique<AccessContextAuditService::CookieAccessHelper>(
691 service());
692 helper->RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
693
694 // Reaccess the cookie at a later time.
695 const base::Time kAccessTime2 =
696 clock()->Now() + base::TimeDelta::FromMinutes(1);
697 clock()->SetNow(kAccessTime2);
698 helper->RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
699
700 // The only record should match the second access.
701 auto records = GetAllAccessRecords();
702 EXPECT_EQ(1u, records.size());
703 CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime2,
704 records);
705
706 // Inform the audit service that the cookie has been deleted, which should
707 // cause the helper to clear it from the set of accessed cookies and remove
708 // the database record.
709 service()->OnCookieChange(
710 net::CookieChangeInfo(*test_cookie, net::CookieAccessResult(),
711 net::CookieChangeCause::EXPLICIT));
712
713 // Flush the helper and ensure that no cookie access is recorded.
714 helper->FlushCookieRecords();
715 records = GetAllAccessRecords();
716 EXPECT_EQ(0u, records.size());
717
718 // Record a cookie access and delete the helper, the access should be flushed
719 // to the service.
720 const base::Time kAccessTime3 =
721 clock()->Now() + base::TimeDelta::FromMinutes(1);
722 clock()->SetNow(kAccessTime3);
723 helper->RecordCookieAccess({*test_cookie}, kTopFrameOrigin);
724
725 helper.reset();
726 records = GetAllAccessRecords();
727 EXPECT_EQ(1u, records.size());
728 CheckContainsCookieRecord(test_cookie.get(), kTopFrameOrigin, kAccessTime3,
729 records);
730 }
731