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