OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/safe_browsing/ui_manager.h" | 5 #include "chrome/browser/safe_browsing/ui_manager.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/callback.h" | 9 #include "base/callback.h" |
10 #include "base/debug/leak_tracker.h" | 10 #include "base/debug/leak_tracker.h" |
(...skipping 28 matching lines...) Expand all Loading... | |
39 using content::NavigationEntry; | 39 using content::NavigationEntry; |
40 using content::WebContents; | 40 using content::WebContents; |
41 using safe_browsing::HitReport; | 41 using safe_browsing::HitReport; |
42 | 42 |
43 namespace { | 43 namespace { |
44 | 44 |
45 const void* const kWhitelistKey = &kWhitelistKey; | 45 const void* const kWhitelistKey = &kWhitelistKey; |
46 | 46 |
47 // A WhitelistUrlSet holds the set of URLs that have been whitelisted for a | 47 // A WhitelistUrlSet holds the set of URLs that have been whitelisted for a |
48 // specific WebContents, along with pending entries that are still undecided. | 48 // specific WebContents, along with pending entries that are still undecided. |
49 // The URLs in this set should come from GetWhitelistUrl() or | |
50 // GetMainFrameWhitelistUrlForResource(). | |
49 class WhitelistUrlSet : public base::SupportsUserData::Data { | 51 class WhitelistUrlSet : public base::SupportsUserData::Data { |
50 public: | 52 public: |
51 WhitelistUrlSet() {} | 53 WhitelistUrlSet() {} |
52 | 54 |
53 bool Contains(const GURL url) { | 55 bool Contains(const GURL url) { return set_.find(url) != set_.end(); } |
54 return set_.find(url.GetWithEmptyPath()) != set_.end(); | 56 |
55 } | 57 void RemovePending(const GURL& url) { pending_.erase(url); } |
56 | 58 |
57 void Insert(const GURL url) { | 59 void Insert(const GURL url) { |
58 set_.insert(url.GetWithEmptyPath()); | 60 set_.insert(url); |
59 pending_.erase(url.GetWithEmptyPath()); | 61 RemovePending(url); |
60 } | 62 } |
61 | 63 |
62 bool ContainsPending(const GURL url) { | 64 bool ContainsPending(const GURL url) { |
63 return pending_.find(url.GetWithEmptyPath()) != pending_.end(); | 65 return pending_.find(url) != pending_.end(); |
64 } | 66 } |
65 | 67 |
66 void InsertPending(const GURL url) { | 68 void InsertPending(const GURL url) { pending_.insert(url); } |
67 pending_.insert(url.GetWithEmptyPath()); | |
68 } | |
69 | 69 |
70 private: | 70 private: |
71 std::set<GURL> set_; | 71 std::set<GURL> set_; |
72 std::set<GURL> pending_; | 72 std::set<GURL> pending_; |
73 | 73 |
74 DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet); | 74 DISALLOW_COPY_AND_ASSIGN(WhitelistUrlSet); |
75 }; | 75 }; |
76 | 76 |
77 // Returns the URL that should be used in a WhitelistUrlSet for the given | |
78 // |resource|. | |
79 GURL GetMainFrameWhitelistUrlForResource( | |
80 const safe_browsing::SafeBrowsingUIManager::UnsafeResource& resource) { | |
81 if (resource.is_subresource) { | |
82 NavigationEntry* entry = resource.GetNavigationEntryForResource(); | |
83 if (!entry) | |
84 return GURL(); | |
85 return entry->GetURL().GetWithEmptyPath(); | |
86 } | |
87 return resource.url.GetWithEmptyPath(); | |
88 } | |
89 | |
90 // Returns the URL that should be used in a WhitelistUrlSet for the | |
91 // resource loaded from |url| on a navigation |entry|. | |
92 GURL GetWhitelistUrl(const GURL& url, | |
93 bool is_subresource, | |
94 NavigationEntry* entry) { | |
95 if (is_subresource) { | |
96 if (!entry) | |
97 return GURL(); | |
98 return entry->GetURL().GetWithEmptyPath(); | |
99 } | |
100 return url.GetWithEmptyPath(); | |
101 } | |
102 | |
77 } // namespace | 103 } // namespace |
78 | 104 |
79 namespace safe_browsing { | 105 namespace safe_browsing { |
80 | 106 |
81 // SafeBrowsingUIManager::UnsafeResource --------------------------------------- | 107 // SafeBrowsingUIManager::UnsafeResource --------------------------------------- |
82 | 108 |
83 SafeBrowsingUIManager::UnsafeResource::UnsafeResource() | 109 SafeBrowsingUIManager::UnsafeResource::UnsafeResource() |
84 : is_subresource(false), | 110 : is_subresource(false), |
85 threat_type(SB_THREAT_TYPE_SAFE), | 111 threat_type(SB_THREAT_TYPE_SAFE), |
86 threat_source(safe_browsing::ThreatSource::UNKNOWN) {} | 112 threat_source(safe_browsing::ThreatSource::UNKNOWN) {} |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
145 if (shutdown) | 171 if (shutdown) |
146 sb_service_ = NULL; | 172 sb_service_ = NULL; |
147 } | 173 } |
148 | 174 |
149 void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) { | 175 void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) { |
150 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time); | 176 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time); |
151 } | 177 } |
152 | 178 |
153 void SafeBrowsingUIManager::OnBlockingPageDone( | 179 void SafeBrowsingUIManager::OnBlockingPageDone( |
154 const std::vector<UnsafeResource>& resources, | 180 const std::vector<UnsafeResource>& resources, |
155 bool proceed) { | 181 bool proceed, |
182 content::WebContents* web_contents, | |
183 const GURL& main_frame_url) { | |
156 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 184 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
157 for (const auto& resource : resources) { | 185 for (const auto& resource : resources) { |
158 if (!resource.callback.is_null()) { | 186 if (!resource.callback.is_null()) { |
159 DCHECK(resource.callback_thread); | 187 DCHECK(resource.callback_thread); |
160 resource.callback_thread->PostTask( | 188 resource.callback_thread->PostTask( |
161 FROM_HERE, base::Bind(resource.callback, proceed)); | 189 FROM_HERE, base::Bind(resource.callback, proceed)); |
162 } | 190 } |
163 | 191 |
164 if (proceed) | 192 GURL whitelist_url = GetWhitelistUrl( |
165 AddToWhitelistUrlSet(resource, false /* Pending -> permanent */); | 193 main_frame_url, false /* is subresource */, |
194 nullptr /* no navigation entry needed for main resource */); | |
195 if (proceed) { | |
196 AddToWhitelistUrlSet(whitelist_url, web_contents, | |
197 false /* Pending -> permanent */); | |
198 } else if (web_contents) { | |
199 // |web_contents| doesn't exist if the tab has been closed. | |
200 RemoveFromPendingWhitelistUrlSet(whitelist_url, web_contents); | |
201 } | |
166 } | 202 } |
167 } | 203 } |
168 | 204 |
169 void SafeBrowsingUIManager::DisplayBlockingPage( | 205 void SafeBrowsingUIManager::DisplayBlockingPage( |
170 const UnsafeResource& resource) { | 206 const UnsafeResource& resource) { |
171 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 207 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
172 if (resource.is_subresource && !resource.is_subframe) { | 208 if (resource.is_subresource && !resource.is_subframe) { |
173 // Sites tagged as serving Unwanted Software should only show a warning for | 209 // Sites tagged as serving Unwanted Software should only show a warning for |
174 // main-frame or sub-frame resource. Similar warning restrictions should be | 210 // main-frame or sub-frame resource. Similar warning restrictions should be |
175 // applied to malware sites tagged as "landing sites" (see "Types of | 211 // applied to malware sites tagged as "landing sites" (see "Types of |
(...skipping 13 matching lines...) Expand all Loading... | |
189 return; | 225 return; |
190 } | 226 } |
191 } | 227 } |
192 | 228 |
193 // The tab might have been closed. If it was closed, just act as if "Don't | 229 // The tab might have been closed. If it was closed, just act as if "Don't |
194 // Proceed" had been chosen. | 230 // Proceed" had been chosen. |
195 WebContents* web_contents = resource.web_contents_getter.Run(); | 231 WebContents* web_contents = resource.web_contents_getter.Run(); |
196 if (!web_contents) { | 232 if (!web_contents) { |
197 std::vector<UnsafeResource> resources; | 233 std::vector<UnsafeResource> resources; |
198 resources.push_back(resource); | 234 resources.push_back(resource); |
199 OnBlockingPageDone(resources, false); | 235 OnBlockingPageDone(resources, false, web_contents, |
236 GetMainFrameWhitelistUrlForResource(resource)); | |
200 return; | 237 return; |
201 } | 238 } |
202 | 239 |
203 // Check if the user has already ignored a SB warning for the same WebContents | 240 // Check if the user has already ignored a SB warning for the same WebContents |
204 // and top-level domain. | 241 // and top-level domain. |
205 if (IsWhitelisted(resource)) { | 242 if (IsWhitelisted(resource)) { |
206 if (!resource.callback.is_null()) { | 243 if (!resource.callback.is_null()) { |
207 DCHECK(resource.callback_thread); | 244 DCHECK(resource.callback_thread); |
208 resource.callback_thread->PostTask(FROM_HERE, | 245 resource.callback_thread->PostTask(FROM_HERE, |
209 base::Bind(resource.callback, true)); | 246 base::Bind(resource.callback, true)); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
245 hit_report.is_metrics_reporting_active = | 282 hit_report.is_metrics_reporting_active = |
246 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(); | 283 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(); |
247 | 284 |
248 MaybeReportSafeBrowsingHit(hit_report); | 285 MaybeReportSafeBrowsingHit(hit_report); |
249 } | 286 } |
250 | 287 |
251 if (resource.threat_type != SB_THREAT_TYPE_SAFE) { | 288 if (resource.threat_type != SB_THREAT_TYPE_SAFE) { |
252 for (Observer& observer : observer_list_) | 289 for (Observer& observer : observer_list_) |
253 observer.OnSafeBrowsingHit(resource); | 290 observer.OnSafeBrowsingHit(resource); |
254 } | 291 } |
255 AddToWhitelistUrlSet(resource, true /* A decision is now pending */); | 292 AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource), |
293 resource.web_contents_getter.Run(), | |
294 true /* A decision is now pending */); | |
256 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource); | 295 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource); |
257 } | 296 } |
258 | 297 |
259 // A safebrowsing hit is sent after a blocking page for malware/phishing | 298 // A safebrowsing hit is sent after a blocking page for malware/phishing |
260 // or after the warning dialog for download urls, only for | 299 // or after the warning dialog for download urls, only for |
261 // UMA || extended_reporting users. | 300 // UMA || extended_reporting users. |
262 void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit( | 301 void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit( |
263 const HitReport& hit_report) { | 302 const HitReport& hit_report) { |
264 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 303 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
265 | 304 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
311 void SafeBrowsingUIManager::AddObserver(Observer* observer) { | 350 void SafeBrowsingUIManager::AddObserver(Observer* observer) { |
312 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 351 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
313 observer_list_.AddObserver(observer); | 352 observer_list_.AddObserver(observer); |
314 } | 353 } |
315 | 354 |
316 void SafeBrowsingUIManager::RemoveObserver(Observer* observer) { | 355 void SafeBrowsingUIManager::RemoveObserver(Observer* observer) { |
317 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 356 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
318 observer_list_.RemoveObserver(observer); | 357 observer_list_.RemoveObserver(observer); |
319 } | 358 } |
320 | 359 |
360 // Static. | |
361 void SafeBrowsingUIManager::CreateWhitelistForTesting( | |
362 content::WebContents* web_contents) { | |
Nathan Parker
2016/10/31 23:54:38
nit: How about putting this code in a "WhitelistUr
estark
2016/11/01 02:41:16
Done.
| |
363 WhitelistUrlSet* site_list = | |
364 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); | |
365 if (!site_list) { | |
366 site_list = new WhitelistUrlSet; | |
367 web_contents->SetUserData(kWhitelistKey, site_list); | |
368 } | |
369 } | |
370 | |
321 void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( | 371 void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( |
322 const std::string& serialized_report) { | 372 const std::string& serialized_report) { |
323 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 373 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
324 | 374 |
325 // The service may delete the ping manager (i.e. when user disabling service, | 375 // The service may delete the ping manager (i.e. when user disabling service, |
326 // etc). This happens on the IO thread. | 376 // etc). This happens on the IO thread. |
327 if (!sb_service_ || !sb_service_->ping_manager()) | 377 if (!sb_service_ || !sb_service_->ping_manager()) |
328 return; | 378 return; |
329 | 379 |
330 sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report); | 380 sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report); |
(...skipping 21 matching lines...) Expand all Loading... | |
352 // etc). This happens on the IO thread. | 402 // etc). This happens on the IO thread. |
353 if (sb_service_.get() == NULL || sb_service_->ping_manager() == NULL) | 403 if (sb_service_.get() == NULL || sb_service_->ping_manager() == NULL) |
354 return; | 404 return; |
355 | 405 |
356 if (!serialized.empty()) { | 406 if (!serialized.empty()) { |
357 DVLOG(1) << "Sending serialized threat details."; | 407 DVLOG(1) << "Sending serialized threat details."; |
358 sb_service_->ping_manager()->ReportThreatDetails(serialized); | 408 sb_service_->ping_manager()->ReportThreatDetails(serialized); |
359 } | 409 } |
360 } | 410 } |
361 | 411 |
362 // Record this domain in the current WebContents as either whitelisted or | 412 // Record this domain in the given WebContents as either whitelisted or |
363 // pending whitelisting (if an interstitial is currently displayed). If an | 413 // pending whitelisting (if an interstitial is currently displayed). If an |
364 // existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet. | 414 // existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet. |
365 void SafeBrowsingUIManager::AddToWhitelistUrlSet(const UnsafeResource& resource, | 415 void SafeBrowsingUIManager::AddToWhitelistUrlSet( |
366 bool pending) { | 416 const GURL& whitelist_url, |
417 content::WebContents* web_contents, | |
418 bool pending) { | |
367 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 419 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
368 | 420 |
369 WebContents* web_contents = resource.web_contents_getter.Run(); | 421 // A WebContents might not exist if the tab has been closed. |
422 if (!web_contents) | |
423 return; | |
424 | |
370 WhitelistUrlSet* site_list = | 425 WhitelistUrlSet* site_list = |
371 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); | 426 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); |
372 if (!site_list) { | 427 if (!site_list) { |
373 site_list = new WhitelistUrlSet; | 428 site_list = new WhitelistUrlSet; |
374 web_contents->SetUserData(kWhitelistKey, site_list); | 429 web_contents->SetUserData(kWhitelistKey, site_list); |
375 } | 430 } |
376 | 431 |
377 GURL whitelisted_url; | 432 if (whitelist_url.is_empty()) |
378 if (resource.is_subresource) { | 433 return; |
379 NavigationEntry* entry = resource.GetNavigationEntryForResource(); | 434 |
380 if (!entry) | 435 if (pending) { |
381 return; | 436 site_list->InsertPending(whitelist_url); |
382 whitelisted_url = entry->GetURL(); | |
383 } else { | 437 } else { |
384 whitelisted_url = resource.url; | 438 site_list->Insert(whitelist_url); |
385 } | 439 } |
386 | 440 |
387 if (pending) { | 441 // Notify security UI that security state has changed. |
388 site_list->InsertPending(whitelisted_url); | 442 web_contents->DidChangeVisibleSecurityState(); |
389 } else { | 443 } |
390 site_list->Insert(whitelisted_url); | 444 |
391 } | 445 void SafeBrowsingUIManager::RemoveFromPendingWhitelistUrlSet( |
446 const GURL& whitelist_url, | |
447 content::WebContents* web_contents) { | |
448 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
449 | |
450 // A WebContents might not exist if the tab has been closed. | |
451 if (!web_contents) | |
452 return; | |
453 | |
454 // Use |web_contents| rather than |resource.web_contents_getter| | |
455 // here. By this point, a "Back" navigation could have already been | |
456 // committed, so the page loading |resource| might be gone and | |
457 // |web_contents_getter| may no longer be valid. | |
458 WhitelistUrlSet* site_list = | |
459 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); | |
460 | |
461 if (whitelist_url.is_empty()) | |
462 return; | |
463 | |
464 // Note that this function does not DCHECK that |whitelist_url| | |
465 // appears in the pending whitelist. In the common case, it's expected | |
466 // that a URL is in the pending whitelist when it is removed, but it's | |
467 // not always the case. For example, if there are several blocking | |
468 // pages queued up for different resources on the same page, and the | |
Nathan Parker
2016/10/31 23:54:38
I thought we didn't queue up several warnings per
estark
2016/11/01 02:41:17
Hrm. I was basing this on https://cs.chromium.org/
estark
2016/11/01 03:00:08
I lied, things do get queued to the UnsafeResource
| |
469 // user goes back to dimiss the first one, the subsequent blocking | |
470 // pages get dismissed as well (as if the user had clicked "Back to | |
471 // safety" on each of them). In this case, the first dismissal will | |
472 // remove the main-frame URL from the pending whitelist, so the | |
473 // main-frame URL will have already been removed when the subsequent | |
474 // blocking pages are dismissed. | |
475 if (site_list->ContainsPending(whitelist_url)) | |
476 site_list->RemovePending(whitelist_url); | |
392 | 477 |
393 // Notify security UI that security state has changed. | 478 // Notify security UI that security state has changed. |
394 web_contents->DidChangeVisibleSecurityState(); | 479 web_contents->DidChangeVisibleSecurityState(); |
395 } | 480 } |
396 | 481 |
397 bool SafeBrowsingUIManager::IsWhitelisted(const UnsafeResource& resource) { | 482 bool SafeBrowsingUIManager::IsWhitelisted(const UnsafeResource& resource) { |
398 NavigationEntry* entry = nullptr; | 483 NavigationEntry* entry = nullptr; |
399 if (resource.is_subresource) { | 484 if (resource.is_subresource) { |
400 entry = resource.GetNavigationEntryForResource(); | 485 entry = resource.GetNavigationEntryForResource(); |
401 } | 486 } |
402 return IsUrlWhitelistedOrPendingForWebContents( | 487 return IsUrlWhitelistedOrPendingForWebContents( |
403 resource.url, resource.is_subresource, entry, | 488 resource.url, resource.is_subresource, entry, |
404 resource.web_contents_getter.Run(), true); | 489 resource.web_contents_getter.Run(), true); |
405 } | 490 } |
406 | 491 |
407 // Check if the user has already seen and/or ignored a SB warning for this | 492 // Check if the user has already seen and/or ignored a SB warning for this |
408 // WebContents and top-level domain. | 493 // WebContents and top-level domain. |
409 bool SafeBrowsingUIManager::IsUrlWhitelistedOrPendingForWebContents( | 494 bool SafeBrowsingUIManager::IsUrlWhitelistedOrPendingForWebContents( |
410 const GURL& url, | 495 const GURL& url, |
411 bool is_subresource, | 496 bool is_subresource, |
412 NavigationEntry* entry, | 497 NavigationEntry* entry, |
413 content::WebContents* web_contents, | 498 content::WebContents* web_contents, |
414 bool whitelist_only) { | 499 bool whitelist_only) { |
415 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 500 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
416 | 501 |
417 GURL lookup_url; | 502 GURL lookup_url = GetWhitelistUrl(url, is_subresource, entry); |
418 if (is_subresource) { | 503 if (lookup_url.is_empty()) |
419 if (!entry) | 504 return false; |
420 return false; | |
421 lookup_url = entry->GetURL(); | |
422 } else { | |
423 lookup_url = url; | |
424 } | |
425 | 505 |
426 WhitelistUrlSet* site_list = | 506 WhitelistUrlSet* site_list = |
427 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); | 507 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); |
428 if (!site_list) | 508 if (!site_list) |
429 return false; | 509 return false; |
430 | 510 |
431 bool whitelisted = site_list->Contains(lookup_url); | 511 bool whitelisted = site_list->Contains(lookup_url); |
432 if (whitelist_only) { | 512 if (whitelist_only) { |
433 return whitelisted; | 513 return whitelisted; |
434 } else { | 514 } else { |
435 return whitelisted || site_list->ContainsPending(lookup_url); | 515 return whitelisted || site_list->ContainsPending(lookup_url); |
436 } | 516 } |
437 } | 517 } |
438 | 518 |
519 // Static. | |
520 GURL SafeBrowsingUIManager::GetMainFrameWhitelistUrlForResourceForTesting( | |
521 const safe_browsing::SafeBrowsingUIManager::UnsafeResource& resource) { | |
522 return GetMainFrameWhitelistUrlForResource(resource); | |
523 } | |
524 | |
439 } // namespace safe_browsing | 525 } // namespace safe_browsing |
OLD | NEW |