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 |
| 103 WhitelistUrlSet* GetOrCreateWhitelist(content::WebContents* web_contents) { |
| 104 WhitelistUrlSet* site_list = |
| 105 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); |
| 106 if (!site_list) { |
| 107 site_list = new WhitelistUrlSet; |
| 108 web_contents->SetUserData(kWhitelistKey, site_list); |
| 109 } |
| 110 return site_list; |
| 111 } |
| 112 |
77 } // namespace | 113 } // namespace |
78 | 114 |
79 namespace safe_browsing { | 115 namespace safe_browsing { |
80 | 116 |
81 // SafeBrowsingUIManager::UnsafeResource --------------------------------------- | 117 // SafeBrowsingUIManager::UnsafeResource --------------------------------------- |
82 | 118 |
83 SafeBrowsingUIManager::UnsafeResource::UnsafeResource() | 119 SafeBrowsingUIManager::UnsafeResource::UnsafeResource() |
84 : is_subresource(false), | 120 : is_subresource(false), |
85 threat_type(SB_THREAT_TYPE_SAFE), | 121 threat_type(SB_THREAT_TYPE_SAFE), |
86 threat_source(safe_browsing::ThreatSource::UNKNOWN) {} | 122 threat_source(safe_browsing::ThreatSource::UNKNOWN) {} |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
145 if (shutdown) | 181 if (shutdown) |
146 sb_service_ = NULL; | 182 sb_service_ = NULL; |
147 } | 183 } |
148 | 184 |
149 void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) { | 185 void SafeBrowsingUIManager::LogPauseDelay(base::TimeDelta time) { |
150 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time); | 186 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time); |
151 } | 187 } |
152 | 188 |
153 void SafeBrowsingUIManager::OnBlockingPageDone( | 189 void SafeBrowsingUIManager::OnBlockingPageDone( |
154 const std::vector<UnsafeResource>& resources, | 190 const std::vector<UnsafeResource>& resources, |
155 bool proceed) { | 191 bool proceed, |
| 192 content::WebContents* web_contents, |
| 193 const GURL& main_frame_url) { |
156 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 194 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
157 for (const auto& resource : resources) { | 195 for (const auto& resource : resources) { |
158 if (!resource.callback.is_null()) { | 196 if (!resource.callback.is_null()) { |
159 DCHECK(resource.callback_thread); | 197 DCHECK(resource.callback_thread); |
160 resource.callback_thread->PostTask( | 198 resource.callback_thread->PostTask( |
161 FROM_HERE, base::Bind(resource.callback, proceed)); | 199 FROM_HERE, base::Bind(resource.callback, proceed)); |
162 } | 200 } |
163 | 201 |
164 if (proceed) | 202 GURL whitelist_url = GetWhitelistUrl( |
165 AddToWhitelistUrlSet(resource, false /* Pending -> permanent */); | 203 main_frame_url, false /* is subresource */, |
| 204 nullptr /* no navigation entry needed for main resource */); |
| 205 if (proceed) { |
| 206 AddToWhitelistUrlSet(whitelist_url, web_contents, |
| 207 false /* Pending -> permanent */); |
| 208 } else if (web_contents) { |
| 209 // |web_contents| doesn't exist if the tab has been closed. |
| 210 RemoveFromPendingWhitelistUrlSet(whitelist_url, web_contents); |
| 211 } |
166 } | 212 } |
167 } | 213 } |
168 | 214 |
169 void SafeBrowsingUIManager::DisplayBlockingPage( | 215 void SafeBrowsingUIManager::DisplayBlockingPage( |
170 const UnsafeResource& resource) { | 216 const UnsafeResource& resource) { |
171 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 217 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
172 if (resource.is_subresource && !resource.is_subframe) { | 218 if (resource.is_subresource && !resource.is_subframe) { |
173 // Sites tagged as serving Unwanted Software should only show a warning for | 219 // Sites tagged as serving Unwanted Software should only show a warning for |
174 // main-frame or sub-frame resource. Similar warning restrictions should be | 220 // main-frame or sub-frame resource. Similar warning restrictions should be |
175 // applied to malware sites tagged as "landing sites" (see "Types of | 221 // applied to malware sites tagged as "landing sites" (see "Types of |
(...skipping 13 matching lines...) Expand all Loading... |
189 return; | 235 return; |
190 } | 236 } |
191 } | 237 } |
192 | 238 |
193 // The tab might have been closed. If it was closed, just act as if "Don't | 239 // The tab might have been closed. If it was closed, just act as if "Don't |
194 // Proceed" had been chosen. | 240 // Proceed" had been chosen. |
195 WebContents* web_contents = resource.web_contents_getter.Run(); | 241 WebContents* web_contents = resource.web_contents_getter.Run(); |
196 if (!web_contents) { | 242 if (!web_contents) { |
197 std::vector<UnsafeResource> resources; | 243 std::vector<UnsafeResource> resources; |
198 resources.push_back(resource); | 244 resources.push_back(resource); |
199 OnBlockingPageDone(resources, false); | 245 OnBlockingPageDone(resources, false, web_contents, |
| 246 GetMainFrameWhitelistUrlForResource(resource)); |
200 return; | 247 return; |
201 } | 248 } |
202 | 249 |
203 // Check if the user has already ignored a SB warning for the same WebContents | 250 // Check if the user has already ignored a SB warning for the same WebContents |
204 // and top-level domain. | 251 // and top-level domain. |
205 if (IsWhitelisted(resource)) { | 252 if (IsWhitelisted(resource)) { |
206 if (!resource.callback.is_null()) { | 253 if (!resource.callback.is_null()) { |
207 DCHECK(resource.callback_thread); | 254 DCHECK(resource.callback_thread); |
208 resource.callback_thread->PostTask(FROM_HERE, | 255 resource.callback_thread->PostTask(FROM_HERE, |
209 base::Bind(resource.callback, true)); | 256 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 = | 292 hit_report.is_metrics_reporting_active = |
246 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(); | 293 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(); |
247 | 294 |
248 MaybeReportSafeBrowsingHit(hit_report); | 295 MaybeReportSafeBrowsingHit(hit_report); |
249 } | 296 } |
250 | 297 |
251 if (resource.threat_type != SB_THREAT_TYPE_SAFE) { | 298 if (resource.threat_type != SB_THREAT_TYPE_SAFE) { |
252 for (Observer& observer : observer_list_) | 299 for (Observer& observer : observer_list_) |
253 observer.OnSafeBrowsingHit(resource); | 300 observer.OnSafeBrowsingHit(resource); |
254 } | 301 } |
255 AddToWhitelistUrlSet(resource, true /* A decision is now pending */); | 302 AddToWhitelistUrlSet(GetMainFrameWhitelistUrlForResource(resource), |
| 303 resource.web_contents_getter.Run(), |
| 304 true /* A decision is now pending */); |
256 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource); | 305 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource); |
257 } | 306 } |
258 | 307 |
259 // A safebrowsing hit is sent after a blocking page for malware/phishing | 308 // A safebrowsing hit is sent after a blocking page for malware/phishing |
260 // or after the warning dialog for download urls, only for | 309 // or after the warning dialog for download urls, only for |
261 // UMA || extended_reporting users. | 310 // UMA || extended_reporting users. |
262 void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit( | 311 void SafeBrowsingUIManager::MaybeReportSafeBrowsingHit( |
263 const HitReport& hit_report) { | 312 const HitReport& hit_report) { |
264 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 313 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
265 | 314 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 void SafeBrowsingUIManager::AddObserver(Observer* observer) { | 360 void SafeBrowsingUIManager::AddObserver(Observer* observer) { |
312 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 361 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
313 observer_list_.AddObserver(observer); | 362 observer_list_.AddObserver(observer); |
314 } | 363 } |
315 | 364 |
316 void SafeBrowsingUIManager::RemoveObserver(Observer* observer) { | 365 void SafeBrowsingUIManager::RemoveObserver(Observer* observer) { |
317 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 366 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
318 observer_list_.RemoveObserver(observer); | 367 observer_list_.RemoveObserver(observer); |
319 } | 368 } |
320 | 369 |
| 370 // Static. |
| 371 void SafeBrowsingUIManager::CreateWhitelistForTesting( |
| 372 content::WebContents* web_contents) { |
| 373 GetOrCreateWhitelist(web_contents); |
| 374 } |
| 375 |
321 void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( | 376 void SafeBrowsingUIManager::ReportInvalidCertificateChainOnIOThread( |
322 const std::string& serialized_report) { | 377 const std::string& serialized_report) { |
323 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 378 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
324 | 379 |
325 // The service may delete the ping manager (i.e. when user disabling service, | 380 // The service may delete the ping manager (i.e. when user disabling service, |
326 // etc). This happens on the IO thread. | 381 // etc). This happens on the IO thread. |
327 if (!sb_service_ || !sb_service_->ping_manager()) | 382 if (!sb_service_ || !sb_service_->ping_manager()) |
328 return; | 383 return; |
329 | 384 |
330 sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report); | 385 sb_service_->ping_manager()->ReportInvalidCertificateChain(serialized_report); |
(...skipping 21 matching lines...) Expand all Loading... |
352 // etc). This happens on the IO thread. | 407 // etc). This happens on the IO thread. |
353 if (sb_service_.get() == NULL || sb_service_->ping_manager() == NULL) | 408 if (sb_service_.get() == NULL || sb_service_->ping_manager() == NULL) |
354 return; | 409 return; |
355 | 410 |
356 if (!serialized.empty()) { | 411 if (!serialized.empty()) { |
357 DVLOG(1) << "Sending serialized threat details."; | 412 DVLOG(1) << "Sending serialized threat details."; |
358 sb_service_->ping_manager()->ReportThreatDetails(serialized); | 413 sb_service_->ping_manager()->ReportThreatDetails(serialized); |
359 } | 414 } |
360 } | 415 } |
361 | 416 |
362 // Record this domain in the current WebContents as either whitelisted or | 417 // Record this domain in the given WebContents as either whitelisted or |
363 // pending whitelisting (if an interstitial is currently displayed). If an | 418 // pending whitelisting (if an interstitial is currently displayed). If an |
364 // existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet. | 419 // existing WhitelistUrlSet does not yet exist, create a new WhitelistUrlSet. |
365 void SafeBrowsingUIManager::AddToWhitelistUrlSet(const UnsafeResource& resource, | 420 void SafeBrowsingUIManager::AddToWhitelistUrlSet( |
366 bool pending) { | 421 const GURL& whitelist_url, |
| 422 content::WebContents* web_contents, |
| 423 bool pending) { |
367 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 424 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
368 | 425 |
369 WebContents* web_contents = resource.web_contents_getter.Run(); | 426 // A WebContents might not exist if the tab has been closed. |
| 427 if (!web_contents) |
| 428 return; |
| 429 |
| 430 WhitelistUrlSet* site_list = GetOrCreateWhitelist(web_contents); |
| 431 |
| 432 if (whitelist_url.is_empty()) |
| 433 return; |
| 434 |
| 435 if (pending) { |
| 436 site_list->InsertPending(whitelist_url); |
| 437 } else { |
| 438 site_list->Insert(whitelist_url); |
| 439 } |
| 440 |
| 441 // Notify security UI that security state has changed. |
| 442 web_contents->DidChangeVisibleSecurityState(); |
| 443 } |
| 444 |
| 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. |
370 WhitelistUrlSet* site_list = | 458 WhitelistUrlSet* site_list = |
371 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); | 459 static_cast<WhitelistUrlSet*>(web_contents->GetUserData(kWhitelistKey)); |
372 if (!site_list) { | |
373 site_list = new WhitelistUrlSet; | |
374 web_contents->SetUserData(kWhitelistKey, site_list); | |
375 } | |
376 | 460 |
377 GURL whitelisted_url; | 461 if (whitelist_url.is_empty()) |
378 if (resource.is_subresource) { | 462 return; |
379 NavigationEntry* entry = resource.GetNavigationEntryForResource(); | |
380 if (!entry) | |
381 return; | |
382 whitelisted_url = entry->GetURL(); | |
383 } else { | |
384 whitelisted_url = resource.url; | |
385 } | |
386 | 463 |
387 if (pending) { | 464 // Note that this function does not DCHECK that |whitelist_url| |
388 site_list->InsertPending(whitelisted_url); | 465 // appears in the pending whitelist. In the common case, it's expected |
389 } else { | 466 // that a URL is in the pending whitelist when it is removed, but it's |
390 site_list->Insert(whitelisted_url); | 467 // not always the case. For example, if there are several blocking |
391 } | 468 // pages queued up for different resources on the same page, and the |
| 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 |