1 // Copyright 2015 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 "content/browser/browsing_data/browsing_data_filter_builder_impl.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <string>
10 #include <vector>
11
12 #include "base/callback.h"
13 #include "base/optional.h"
14 #include "net/cookies/canonical_cookie.h"
15 #include "net/cookies/cookie_deletion_info.h"
16 #include "services/network/cookie_manager.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "url/gurl.h"
20 #include "url/origin.h"
21
22 using CookieDeletionInfo = net::CookieDeletionInfo;
23
24 namespace content {
25
26 namespace {
27
28 const char kGoogleDomain[] = "google.com";
29 // sp.nom.br is an eTLD, so this is a regular valid registrable domain, just
30 // like google.com.
31 const char kLongETLDDomain[] = "website.sp.nom.br";
32 // This domain will also not be found in registries, and since it has only
33 // one component, it will not be recognized as a valid registrable domain.
34 const char kInternalHostname[] = "fileserver";
35 // This domain will not be found in registries. It will be assumed that
36 // it belongs to an unknown registry, and since it has two components,
37 // they will be treated as the second level domain and TLD. Most importantly,
38 // it will NOT be treated as a subdomain of "fileserver".
39 const char kUnknownRegistryDomain[] = "second-level-domain.fileserver";
40 // IP addresses are supported.
41 const char kIPAddress[] = "192.168.1.1";
42
43 struct TestCase {
44 std::string url;
45 bool should_match;
46 };
47
RunTestCase(TestCase test_case,const base::RepeatingCallback<bool (const GURL &)> & filter)48 void RunTestCase(TestCase test_case,
49 const base::RepeatingCallback<bool(const GURL&)>& filter) {
50 GURL url(test_case.url);
51 EXPECT_TRUE(url.is_valid()) << test_case.url << " is not valid.";
52 EXPECT_EQ(test_case.should_match, filter.Run(GURL(test_case.url)))
53 << test_case.url;
54 }
55
RunTestCase(TestCase test_case,network::mojom::CookieDeletionFilterPtr deletion_filter)56 void RunTestCase(TestCase test_case,
57 network::mojom::CookieDeletionFilterPtr deletion_filter) {
58 // Test with regular cookie, http only, domain, and secure.
59 CookieDeletionInfo delete_info =
60 network::DeletionFilterToInfo(std::move(deletion_filter));
61 std::string cookie_line = "A=2";
62 GURL test_url(test_case.url);
63 EXPECT_TRUE(test_url.is_valid()) << test_case.url;
64 std::unique_ptr<net::CanonicalCookie> cookie =
65 net::CanonicalCookie::Create(test_url, cookie_line, base::Time::Now(),
66 base::nullopt /* server_time */);
67 EXPECT_TRUE(cookie) << cookie_line << " from " << test_case.url
68 << " is not a valid cookie";
69 if (cookie)
70 EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie))
71 << cookie->DebugString();
72
73 cookie_line = std::string("A=2;domain=") + test_url.host();
74 cookie =
75 net::CanonicalCookie::Create(test_url, cookie_line, base::Time::Now(),
76 base::nullopt /* server_time */);
77 if (cookie)
78 EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie))
79 << cookie->DebugString();
80
81 cookie_line = std::string("A=2; HttpOnly;") + test_url.host();
82 cookie =
83 net::CanonicalCookie::Create(test_url, cookie_line, base::Time::Now(),
84 base::nullopt /* server_time */);
85 if (cookie)
86 EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie))
87 << cookie->DebugString();
88
89 cookie_line = std::string("A=2; HttpOnly; Secure;") + test_url.host();
90 cookie =
91 net::CanonicalCookie::Create(test_url, cookie_line, base::Time::Now(),
92 base::nullopt /* server_time */);
93 if (cookie)
94 EXPECT_EQ(test_case.should_match, delete_info.Matches(*cookie))
95 << cookie->DebugString();
96 }
97
RunTestCase(TestCase test_case,const base::RepeatingCallback<bool (const std::string &)> & filter)98 void RunTestCase(
99 TestCase test_case,
100 const base::RepeatingCallback<bool(const std::string&)>& filter) {
101 std::string channel_id_server_id = test_case.url;
102 EXPECT_EQ(test_case.should_match, filter.Run(channel_id_server_id))
103 << channel_id_server_id << " should "
104 << (test_case.should_match ? "" : "NOT ") << "be matched by the filter.";
105 }
106
107 } // namespace
108
TEST(BrowsingDataFilterBuilderImplTest,Noop)109 TEST(BrowsingDataFilterBuilderImplTest, Noop) {
110 // An no-op filter matches everything.
111 base::RepeatingCallback<bool(const GURL&)> filter =
112 BrowsingDataFilterBuilder::BuildNoopFilter();
113
114 TestCase test_cases[] = {
115 {"https://www.google.com", true},
116 {"https://www.chrome.com", true},
117 {"http://www.google.com/foo/bar", true},
118 {"https://website.sp.nom.br", true},
119 };
120
121 for (TestCase test_case : test_cases)
122 RunTestCase(test_case, filter);
123 }
124
TEST(BrowsingDataFilterBuilderImplTest,RegistrableDomainGURLWhitelist)125 TEST(BrowsingDataFilterBuilderImplTest,
126 RegistrableDomainGURLWhitelist) {
127 BrowsingDataFilterBuilderImpl builder(
128 BrowsingDataFilterBuilderImpl::WHITELIST);
129 builder.AddRegisterableDomain(std::string(kGoogleDomain));
130 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
131 builder.AddRegisterableDomain(std::string(kIPAddress));
132 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
133 builder.AddRegisterableDomain(std::string(kInternalHostname));
134 base::RepeatingCallback<bool(const GURL&)> filter =
135 builder.BuildGeneralFilter();
136
137 TestCase test_cases[] = {
138 // We match any URL on the specified domains.
139 {"http://www.google.com/foo/bar", true},
140 {"https://www.sub.google.com/foo/bar", true},
141 {"https://sub.google.com", true},
142 {"http://www.sub.google.com:8000/foo/bar", true},
143 {"https://website.sp.nom.br", true},
144 {"https://www.website.sp.nom.br", true},
145 {"http://192.168.1.1", true},
146 {"http://192.168.1.1:80", true},
147
148 // Internal hostnames do not have subdomains.
149 {"http://fileserver", true },
150 {"http://fileserver/foo/bar", true },
151 {"http://website.fileserver/foo/bar", false },
152
153 // This is a valid registrable domain with the TLD "fileserver", which
154 // is unrelated to the internal hostname "fileserver".
155 {"http://second-level-domain.fileserver/foo", true},
156 {"http://www.second-level-domain.fileserver/index.html", true},
157
158 // Different domains.
159 {"https://www.youtube.com", false},
160 {"https://www.google.net", false},
161 {"http://192.168.1.2", false},
162
163 // Check both a bare eTLD.
164 {"https://sp.nom.br", false},
165 };
166
167 for (TestCase test_case : test_cases)
168 RunTestCase(test_case, filter);
169 }
170
TEST(BrowsingDataFilterBuilderImplTest,RegistrableDomainGURLBlacklist)171 TEST(BrowsingDataFilterBuilderImplTest,
172 RegistrableDomainGURLBlacklist) {
173 BrowsingDataFilterBuilderImpl builder(
174 BrowsingDataFilterBuilderImpl::BLACKLIST);
175 builder.AddRegisterableDomain(std::string(kGoogleDomain));
176 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
177 builder.AddRegisterableDomain(std::string(kIPAddress));
178 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
179 builder.AddRegisterableDomain(std::string(kInternalHostname));
180 base::RepeatingCallback<bool(const GURL&)> filter =
181 builder.BuildGeneralFilter();
182
183 TestCase test_cases[] = {
184 // We match any URL that are not on the specified domains.
185 {"http://www.google.com/foo/bar", false},
186 {"https://www.sub.google.com/foo/bar", false},
187 {"https://sub.google.com", false},
188 {"http://www.sub.google.com:8000/foo/bar", false},
189 {"https://website.sp.nom.br", false},
190 {"https://www.website.sp.nom.br", false},
191 {"http://192.168.1.1", false},
192 {"http://192.168.1.1:80", false},
193
194 // Internal hostnames do not have subdomains.
195 {"http://fileserver", false },
196 {"http://fileserver/foo/bar", false },
197 {"http://website.fileserver/foo/bar", true },
198
199 // This is a valid registrable domain with the TLD "fileserver", which
200 // is unrelated to the internal hostname "fileserver".
201 {"http://second-level-domain.fileserver/foo", false},
202 {"http://www.second-level-domain.fileserver/index.html", false},
203
204 // Different domains.
205 {"https://www.youtube.com", true},
206 {"https://www.google.net", true},
207 {"http://192.168.1.2", true},
208
209 // Check our bare eTLD.
210 {"https://sp.nom.br", true},
211 };
212
213 for (TestCase test_case : test_cases)
214 RunTestCase(test_case, filter);
215 }
216
TEST(BrowsingDataFilterBuilderImplTest,RegistrableDomainMatchesCookiesWhitelist)217 TEST(BrowsingDataFilterBuilderImplTest,
218 RegistrableDomainMatchesCookiesWhitelist) {
219 BrowsingDataFilterBuilderImpl builder(
220 BrowsingDataFilterBuilderImpl::WHITELIST);
221 builder.AddRegisterableDomain(std::string(kGoogleDomain));
222 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
223 builder.AddRegisterableDomain(std::string(kIPAddress));
224 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
225 builder.AddRegisterableDomain(std::string(kInternalHostname));
226
227 TestCase test_cases[] = {
228 // Any cookie with the same registerable domain as the origins is matched.
229 {"https://www.google.com", true},
230 {"http://www.google.com", true},
231 {"http://www.google.com:300", true},
232 {"https://mail.google.com", true},
233 {"http://mail.google.com", true},
234 {"http://google.com", true},
235 {"https://website.sp.nom.br", true},
236 {"https://sub.website.sp.nom.br", true},
237 {"http://192.168.1.1", true},
238 {"http://192.168.1.1:10", true},
239
240 // Different eTLDs.
241 {"https://www.google.org", false},
242 {"https://www.google.co.uk", false},
243
244 // We treat eTLD+1 and bare eTLDs as different domains.
245 {"https://www.sp.nom.br", false},
246 {"https://sp.nom.br", false},
247
248 // Different hosts in general.
249 {"https://www.chrome.com", false},
250 {"http://192.168.2.1", false},
251
252 // Internal hostnames do not have subdomains.
253 {"https://fileserver", true },
254 {"http://fileserver/foo/bar", true },
255 {"http://website.fileserver", false },
256
257 // This is a valid registrable domain with the TLD "fileserver", which
258 // is unrelated to the internal hostname "fileserver".
259 {"http://second-level-domain.fileserver", true},
260 {"https://subdomain.second-level-domain.fileserver", true},
261 };
262
263 for (TestCase test_case : test_cases)
264 RunTestCase(test_case, builder.BuildCookieDeletionFilter());
265 }
266
TEST(BrowsingDataFilterBuilderImplTest,RegistrableDomainMatchesCookiesBlacklist)267 TEST(BrowsingDataFilterBuilderImplTest,
268 RegistrableDomainMatchesCookiesBlacklist) {
269 BrowsingDataFilterBuilderImpl builder(
270 BrowsingDataFilterBuilderImpl::BLACKLIST);
271 builder.AddRegisterableDomain(std::string(kGoogleDomain));
272 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
273 builder.AddRegisterableDomain(std::string(kIPAddress));
274 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
275 builder.AddRegisterableDomain(std::string(kInternalHostname));
276
277 TestCase test_cases[] = {
278 // Any cookie that doesn't have the same registerable domain is matched.
279 {"https://www.google.com", false},
280 {"http://www.google.com", false},
281 {"http://www.google.com:300", false},
282 {"https://mail.google.com", false},
283 {"http://mail.google.com", false},
284 {"http://google.com", false},
285 {"https://website.sp.nom.br", false},
286 {"https://sub.website.sp.nom.br", false},
287 {"http://192.168.1.1", false},
288 {"http://192.168.1.1:10", false},
289
290 // Different eTLDs.
291 {"https://www.google.org", true},
292 {"https://www.google.co.uk", true},
293
294 // We treat eTLD+1 and bare eTLDs as different domains.
295 {"https://www.sp.nom.br", true},
296 {"https://sp.nom.br", true},
297
298 // Different hosts in general.
299 {"https://www.chrome.com", true},
300 {"http://192.168.2.1", true},
301
302 // Internal hostnames do not have subdomains.
303 {"https://fileserver", false },
304 {"http://fileserver/foo/bar", false },
305 {"http://website.fileserver", true },
306
307 // This is a valid registrable domain with the TLD "fileserver", which
308 // is unrelated to the internal hostname "fileserver".
309 {"http://second-level-domain.fileserver", false},
310 {"https://subdomain.second-level-domain.fileserver", false},
311 };
312
313 for (TestCase test_case : test_cases)
314 RunTestCase(test_case, builder.BuildCookieDeletionFilter());
315 }
316
TEST(BrowsingDataFilterBuilderImplTest,NetworkServiceFilterWhitelist)317 TEST(BrowsingDataFilterBuilderImplTest, NetworkServiceFilterWhitelist) {
318 BrowsingDataFilterBuilderImpl builder(
319 BrowsingDataFilterBuilderImpl::WHITELIST);
320 ASSERT_EQ(BrowsingDataFilterBuilderImpl::WHITELIST, builder.GetMode());
321 builder.AddRegisterableDomain(std::string(kGoogleDomain));
322 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
323 builder.AddRegisterableDomain(std::string(kIPAddress));
324 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
325 builder.AddRegisterableDomain(std::string(kInternalHostname));
326 network::mojom::ClearDataFilterPtr filter =
327 builder.BuildNetworkServiceFilter();
328
329 EXPECT_EQ(network::mojom::ClearDataFilter_Type::DELETE_MATCHES, filter->type);
330 EXPECT_THAT(filter->domains, testing::UnorderedElementsAre(
331 kGoogleDomain, kLongETLDDomain, kIPAddress,
332 kUnknownRegistryDomain, kInternalHostname));
333 EXPECT_TRUE(filter->origins.empty());
334 }
335
TEST(BrowsingDataFilterBuilderImplTest,NetworkServiceFilterBlacklist)336 TEST(BrowsingDataFilterBuilderImplTest, NetworkServiceFilterBlacklist) {
337 BrowsingDataFilterBuilderImpl builder(
338 BrowsingDataFilterBuilderImpl::BLACKLIST);
339 ASSERT_EQ(BrowsingDataFilterBuilderImpl::BLACKLIST, builder.GetMode());
340 builder.AddRegisterableDomain(std::string(kGoogleDomain));
341 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
342 builder.AddRegisterableDomain(std::string(kIPAddress));
343 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
344 builder.AddRegisterableDomain(std::string(kInternalHostname));
345 network::mojom::ClearDataFilterPtr filter =
346 builder.BuildNetworkServiceFilter();
347
348 EXPECT_EQ(network::mojom::ClearDataFilter_Type::KEEP_MATCHES, filter->type);
349 EXPECT_THAT(filter->domains, testing::UnorderedElementsAre(
350 kGoogleDomain, kLongETLDDomain, kIPAddress,
351 kUnknownRegistryDomain, kInternalHostname));
352 EXPECT_TRUE(filter->origins.empty());
353 }
354
TEST(BrowsingDataFilterBuilderImplTest,RegistrableDomainMatchesPluginSitesWhitelist)355 TEST(BrowsingDataFilterBuilderImplTest,
356 RegistrableDomainMatchesPluginSitesWhitelist) {
357 BrowsingDataFilterBuilderImpl builder(
358 BrowsingDataFilterBuilderImpl::WHITELIST);
359 builder.AddRegisterableDomain(std::string(kGoogleDomain));
360 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
361 builder.AddRegisterableDomain(std::string(kIPAddress));
362 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
363 builder.AddRegisterableDomain(std::string(kInternalHostname));
364 base::RepeatingCallback<bool(const std::string&)> filter =
365 builder.BuildPluginFilter();
366
367 TestCase test_cases[] = {
368 // Plugin sites can be domains, ...
369 {"google.com", true},
370 {"www.google.com", true},
371 {"website.sp.nom.br", true},
372 {"www.website.sp.nom.br", true},
373 {"second-level-domain.fileserver", true},
374 {"foo.bar.second-level-domain.fileserver", true},
375
376 // ... IP addresses, or internal hostnames.
377 {"192.168.1.1", true},
378 {"fileserver", true},
379
380 // Sites not in the whitelist are not matched.
381 {"example.com", false},
382 {"192.168.1.2", false},
383 {"website.fileserver", false},
384 };
385
386 for (TestCase test_case : test_cases)
387 RunTestCase(test_case, filter);
388 }
389
TEST(BrowsingDataFilterBuilderImplTest,RegistrableDomainMatchesPluginSitesBlacklist)390 TEST(BrowsingDataFilterBuilderImplTest,
391 RegistrableDomainMatchesPluginSitesBlacklist) {
392 BrowsingDataFilterBuilderImpl builder(
393 BrowsingDataFilterBuilderImpl::BLACKLIST);
394 builder.AddRegisterableDomain(std::string(kGoogleDomain));
395 builder.AddRegisterableDomain(std::string(kLongETLDDomain));
396 builder.AddRegisterableDomain(std::string(kIPAddress));
397 builder.AddRegisterableDomain(std::string(kUnknownRegistryDomain));
398 builder.AddRegisterableDomain(std::string(kInternalHostname));
399 base::RepeatingCallback<bool(const std::string&)> filter =
400 builder.BuildPluginFilter();
401
402 TestCase test_cases[] = {
403 // Plugin sites can be domains, ...
404 {"google.com", false},
405 {"www.google.com", false},
406 {"website.sp.nom.br", false},
407 {"www.website.sp.nom.br", false},
408 {"second-level-domain.fileserver", false},
409 {"foo.bar.second-level-domain.fileserver", false},
410
411 // ... IP addresses, or internal hostnames.
412 {"192.168.1.1", false},
413 {"fileserver", false},
414
415 // Sites not in the blacklist are matched.
416 {"example.com", true},
417 {"192.168.1.2", true},
418 {"website.fileserver", true},
419 };
420
421 for (TestCase test_case : test_cases)
422 RunTestCase(test_case, filter);
423 }
424
TEST(BrowsingDataFilterBuilderImplTest,OriginWhitelist)425 TEST(BrowsingDataFilterBuilderImplTest, OriginWhitelist) {
426 BrowsingDataFilterBuilderImpl builder(
427 BrowsingDataFilterBuilderImpl::WHITELIST);
428 builder.AddOrigin(url::Origin::Create(GURL("https://www.google.com")));
429 builder.AddOrigin(url::Origin::Create(GURL("http://www.example.com")));
430 base::RepeatingCallback<bool(const GURL&)> filter =
431 builder.BuildGeneralFilter();
432
433 TestCase test_cases[] = {
434 // Whitelist matches any URL on the specified origins.
435 { "https://www.google.com", true },
436 { "https://www.google.com/?q=test", true },
437 { "http://www.example.com", true },
438 { "http://www.example.com/index.html", true },
439 { "http://www.example.com/foo/bar", true },
440
441 // Subdomains are different origins.
442 { "https://test.www.google.com", false },
443
444 // Different scheme or port is a different origin.
445 { "https://www.google.com:8000", false },
446 { "https://www.example.com/index.html", false },
447
448 // Different host is a different origin.
449 { "https://www.youtube.com", false },
450 { "https://www.chromium.org", false },
451 };
452
453 for (TestCase test_case : test_cases)
454 RunTestCase(test_case, filter);
455 }
456
TEST(BrowsingDataFilterBuilderImplTest,OriginBlacklist)457 TEST(BrowsingDataFilterBuilderImplTest, OriginBlacklist) {
458 BrowsingDataFilterBuilderImpl builder(
459 BrowsingDataFilterBuilderImpl::BLACKLIST);
460 builder.AddOrigin(url::Origin::Create(GURL("https://www.google.com")));
461 builder.AddOrigin(url::Origin::Create(GURL("http://www.example.com")));
462 base::RepeatingCallback<bool(const GURL&)> filter =
463 builder.BuildGeneralFilter();
464
465 TestCase test_cases[] = {
466 // URLS on explicitly specified origins are not matched.
467 { "https://www.google.com", false },
468 { "https://www.google.com/?q=test", false },
469 { "http://www.example.com", false },
470 { "http://www.example.com/index.html", false },
471 { "http://www.example.com/foo/bar", false },
472
473 // Subdomains are different origins.
474 { "https://test.www.google.com", true },
475
476 // The same hosts but with different schemes and ports
477 // are not blacklisted.
478 { "https://www.google.com:8000", true },
479 { "https://www.example.com/index.html", true },
480
481 // Different hosts are not blacklisted.
482 { "https://www.chrome.com", true },
483 { "https://www.youtube.com", true },
484 };
485
486 for (TestCase test_case : test_cases)
487 RunTestCase(test_case, filter);
488 }
489
TEST(BrowsingDataFilterBuilderImplTest,CombinedWhitelist)490 TEST(BrowsingDataFilterBuilderImplTest, CombinedWhitelist) {
491 BrowsingDataFilterBuilderImpl builder(
492 BrowsingDataFilterBuilderImpl::WHITELIST);
493 builder.AddOrigin(url::Origin::Create(GURL("https://google.com")));
494 builder.AddRegisterableDomain("example.com");
495 base::RepeatingCallback<bool(const GURL&)> filter =
496 builder.BuildGeneralFilter();
497
498 TestCase test_cases[] = {
499 // Whitelist matches any URL on the specified origins.
500 { "https://google.com/foo/bar", true },
501 { "https://example.com/?q=test", true },
502
503 // Since www.google.com was added as an origin, its subdomains are not
504 // matched. However, example.com was added as a registrable domain,
505 // so its subdomains are matched.
506 { "https://www.google.com/foo/bar", false },
507 { "https://www.example.com/?q=test", true },
508 };
509
510 for (TestCase test_case : test_cases)
511 RunTestCase(test_case, filter);
512 }
513
TEST(BrowsingDataFilterBuilderImplTest,CombinedBlacklist)514 TEST(BrowsingDataFilterBuilderImplTest, CombinedBlacklist) {
515 BrowsingDataFilterBuilderImpl builder(
516 BrowsingDataFilterBuilderImpl::BLACKLIST);
517 builder.AddOrigin(url::Origin::Create(GURL("https://google.com")));
518 builder.AddRegisterableDomain("example.com");
519 base::RepeatingCallback<bool(const GURL&)> filter =
520 builder.BuildGeneralFilter();
521
522 TestCase test_cases[] = {
523 // URLS on explicitly specified origins are not matched.
524 { "https://google.com/foo/bar", false },
525 { "https://example.com/?q=test", false },
526
527 // Since www.google.com was added as an origin, its subdomains are
528 // not in the blacklist. However, example.com was added as a registrable
529 // domain, so its subdomains are also blacklisted.
530 { "https://www.google.com/foo/bar", true },
531 { "https://www.example.com/?q=test", false },
532 };
533
534 for (TestCase test_case : test_cases)
535 RunTestCase(test_case, filter);
536 }
537
538 } // namespace content
539