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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/safe_browsing/client_side_detection_host.h" 6 7 #include <vector> 8 9 #include "base/logging.h" 10 #include "base/memory/ref_counted.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/metrics/histogram.h" 13 #include "base/prefs/pref_service.h" 14 #include "base/sequenced_task_runner_helpers.h" 15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/safe_browsing/browser_feature_extractor.h" 18 #include "chrome/browser/safe_browsing/client_side_detection_service.h" 19 #include "chrome/browser/safe_browsing/database_manager.h" 20 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "chrome/common/chrome_version_info.h" 23 #include "chrome/common/pref_names.h" 24 #include "chrome/common/safe_browsing/csd.pb.h" 25 #include "chrome/common/safe_browsing/safebrowsing_messages.h" 26 #include "content/public/browser/browser_thread.h" 27 #include "content/public/browser/navigation_controller.h" 28 #include "content/public/browser/navigation_details.h" 29 #include "content/public/browser/navigation_entry.h" 30 #include "content/public/browser/notification_details.h" 31 #include "content/public/browser/notification_source.h" 32 #include "content/public/browser/notification_types.h" 33 #include "content/public/browser/render_process_host.h" 34 #include "content/public/browser/render_view_host.h" 35 #include "content/public/browser/resource_request_details.h" 36 #include "content/public/browser/web_contents.h" 37 #include "content/public/common/frame_navigate_params.h" 38 #include "url/gurl.h" 39 40 using content::BrowserThread; 41 using content::NavigationEntry; 42 using content::ResourceRequestDetails; 43 using content::WebContents; 44 45 namespace safe_browsing { 46 47 const int ClientSideDetectionHost::kMaxUrlsPerIP = 20; 48 const int ClientSideDetectionHost::kMaxIPsPerBrowse = 200; 49 50 // This class is instantiated each time a new toplevel URL loads, and 51 // asynchronously checks whether the phishing classifier should run for this 52 // URL. If so, it notifies the renderer with a StartPhishingDetection IPC. 53 // Objects of this class are ref-counted and will be destroyed once nobody 54 // uses it anymore. If |web_contents|, |csd_service| or |host| go away you need 55 // to call Cancel(). We keep the |database_manager| alive in a ref pointer for 56 // as long as it takes. 57 class ClientSideDetectionHost::ShouldClassifyUrlRequest 58 : public base::RefCountedThreadSafe< 59 ClientSideDetectionHost::ShouldClassifyUrlRequest> { 60 public: 61 ShouldClassifyUrlRequest(const content::FrameNavigateParams& params, 62 WebContents* web_contents, 63 ClientSideDetectionService* csd_service, 64 SafeBrowsingDatabaseManager* database_manager, 65 ClientSideDetectionHost* host) 66 : canceled_(false), 67 params_(params), 68 web_contents_(web_contents), 69 csd_service_(csd_service), 70 database_manager_(database_manager), 71 host_(host) { 72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 73 DCHECK(web_contents_); 74 DCHECK(csd_service_); 75 DCHECK(database_manager_.get()); 76 DCHECK(host_); 77 } 78 79 void Start() { 80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 81 82 // We start by doing some simple checks that can run on the UI thread. 83 UMA_HISTOGRAM_COUNTS("SBClientPhishing.ClassificationStart", 1); 84 85 // Only classify [X]HTML documents. 86 if (params_.contents_mime_type != "text/html" && 87 params_.contents_mime_type != "application/xhtml+xml") { 88 VLOG(1) << "Skipping phishing classification for URL: " << params_.url 89 << " because it has an unsupported MIME type: " 90 << params_.contents_mime_type; 91 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail", 92 NO_CLASSIFY_UNSUPPORTED_MIME_TYPE, 93 NO_CLASSIFY_MAX); 94 return; 95 } 96 97 if (csd_service_->IsPrivateIPAddress(params_.socket_address.host())) { 98 VLOG(1) << "Skipping phishing classification for URL: " << params_.url 99 << " because of hosting on private IP: " 100 << params_.socket_address.host(); 101 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail", 102 NO_CLASSIFY_PRIVATE_IP, 103 NO_CLASSIFY_MAX); 104 return; 105 } 106 107 // Don't run the phishing classifier if the tab is incognito. 108 if (web_contents_->GetBrowserContext()->IsOffTheRecord()) { 109 VLOG(1) << "Skipping phishing classification for URL: " << params_.url 110 << " because we're browsing incognito."; 111 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail", 112 NO_CLASSIFY_OFF_THE_RECORD, 113 NO_CLASSIFY_MAX); 114 115 return; 116 } 117 118 // We lookup the csd-whitelist before we lookup the cache because 119 // a URL may have recently been whitelisted. If the URL matches 120 // the csd-whitelist we won't start classification. The 121 // csd-whitelist check has to be done on the IO thread because it 122 // uses the SafeBrowsing service class. 123 BrowserThread::PostTask( 124 BrowserThread::IO, 125 FROM_HERE, 126 base::Bind(&ShouldClassifyUrlRequest::CheckCsdWhitelist, 127 this, params_.url)); 128 } 129 130 void Cancel() { 131 canceled_ = true; 132 // Just to make sure we don't do anything stupid we reset all these 133 // pointers except for the safebrowsing service class which may be 134 // accessed by CheckCsdWhitelist(). 135 web_contents_ = NULL; 136 csd_service_ = NULL; 137 host_ = NULL; 138 } 139 140 private: 141 friend class base::RefCountedThreadSafe< 142 ClientSideDetectionHost::ShouldClassifyUrlRequest>; 143 144 // Enum used to keep stats about why the pre-classification check failed. 145 enum PreClassificationCheckFailures { 146 OBSOLETE_NO_CLASSIFY_PROXY_FETCH, 147 NO_CLASSIFY_PRIVATE_IP, 148 NO_CLASSIFY_OFF_THE_RECORD, 149 NO_CLASSIFY_MATCH_CSD_WHITELIST, 150 NO_CLASSIFY_TOO_MANY_REPORTS, 151 NO_CLASSIFY_UNSUPPORTED_MIME_TYPE, 152 153 NO_CLASSIFY_MAX // Always add new values before this one. 154 }; 155 156 // The destructor can be called either from the UI or the IO thread. 157 virtual ~ShouldClassifyUrlRequest() { } 158 159 void CheckCsdWhitelist(const GURL& url) { 160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 161 if (!database_manager_.get() || 162 database_manager_->MatchCsdWhitelistUrl(url)) { 163 // We're done. There is no point in going back to the UI thread. 164 VLOG(1) << "Skipping phishing classification for URL: " << url 165 << " because it matches the csd whitelist"; 166 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail", 167 NO_CLASSIFY_MATCH_CSD_WHITELIST, 168 NO_CLASSIFY_MAX); 169 return; 170 } 171 172 bool malware_killswitch_on = database_manager_->IsMalwareKillSwitchOn(); 173 174 BrowserThread::PostTask( 175 BrowserThread::UI, 176 FROM_HERE, 177 base::Bind(&ShouldClassifyUrlRequest::CheckCache, this, 178 malware_killswitch_on)); 179 } 180 181 void CheckCache(bool malware_killswitch_on) { 182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 183 if (canceled_) { 184 return; 185 } 186 187 host_->SetMalwareKillSwitch(malware_killswitch_on); 188 // If result is cached, we don't want to run classification again 189 bool is_phishing; 190 if (csd_service_->GetValidCachedResult(params_.url, &is_phishing)) { 191 VLOG(1) << "Satisfying request for " << params_.url << " from cache"; 192 UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestSatisfiedFromCache", 1); 193 // Since we are already on the UI thread, this is safe. 194 host_->MaybeShowPhishingWarning(params_.url, is_phishing); 195 return; 196 } 197 198 // We want to limit the number of requests, though we will ignore the 199 // limit for urls in the cache. We don't want to start classifying 200 // too many pages as phishing, but for those that we already think are 201 // phishing we want to give ourselves a chance to fix false positives. 202 if (csd_service_->IsInCache(params_.url)) { 203 VLOG(1) << "Reporting limit skipped for " << params_.url 204 << " as it was in the cache."; 205 UMA_HISTOGRAM_COUNTS("SBClientPhishing.ReportLimitSkipped", 1); 206 } else if (csd_service_->OverPhishingReportLimit()) { 207 VLOG(1) << "Too many report phishing requests sent recently, " 208 << "not running classification for " << params_.url; 209 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail", 210 NO_CLASSIFY_TOO_MANY_REPORTS, 211 NO_CLASSIFY_MAX); 212 return; 213 } 214 215 // Everything checks out, so start classification. 216 // |web_contents_| is safe to call as we will be destructed 217 // before it is. 218 VLOG(1) << "Instruct renderer to start phishing detection for URL: " 219 << params_.url; 220 content::RenderViewHost* rvh = web_contents_->GetRenderViewHost(); 221 rvh->Send(new SafeBrowsingMsg_StartPhishingDetection( 222 rvh->GetRoutingID(), params_.url)); 223 } 224 225 // No need to protect |canceled_| with a lock because it is only read and 226 // written by the UI thread. 227 bool canceled_; 228 content::FrameNavigateParams params_; 229 WebContents* web_contents_; 230 ClientSideDetectionService* csd_service_; 231 // We keep a ref pointer here just to make sure the safe browsing 232 // database manager stays alive long enough. 233 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; 234 ClientSideDetectionHost* host_; 235 236 DISALLOW_COPY_AND_ASSIGN(ShouldClassifyUrlRequest); 237 }; 238 239 // static 240 ClientSideDetectionHost* ClientSideDetectionHost::Create( 241 WebContents* tab) { 242 return new ClientSideDetectionHost(tab); 243 } 244 245 ClientSideDetectionHost::ClientSideDetectionHost(WebContents* tab) 246 : content::WebContentsObserver(tab), 247 csd_service_(NULL), 248 weak_factory_(this), 249 unsafe_unique_page_id_(-1), 250 malware_killswitch_on_(false), 251 malware_report_enabled_(false), 252 malware_or_phishing_match_(false) { 253 DCHECK(tab); 254 // Note: csd_service_ and sb_service will be NULL here in testing. 255 csd_service_ = g_browser_process->safe_browsing_detection_service(); 256 feature_extractor_.reset(new BrowserFeatureExtractor(tab, this)); 257 registrar_.Add(this, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED, 258 content::Source<WebContents>(tab)); 259 260 scoped_refptr<SafeBrowsingService> sb_service = 261 g_browser_process->safe_browsing_service(); 262 if (sb_service.get()) { 263 ui_manager_ = sb_service->ui_manager(); 264 database_manager_ = sb_service->database_manager(); 265 ui_manager_->AddObserver(this); 266 } 267 268 // Only enable the malware bad IP matching and report feature for canary 269 // and dev channel. 270 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 271 malware_report_enabled_ = ( 272 channel == chrome::VersionInfo::CHANNEL_DEV || 273 channel == chrome::VersionInfo::CHANNEL_CANARY); 274 } 275 276 ClientSideDetectionHost::~ClientSideDetectionHost() { 277 if (ui_manager_.get()) 278 ui_manager_->RemoveObserver(this); 279 } 280 281 bool ClientSideDetectionHost::OnMessageReceived(const IPC::Message& message) { 282 bool handled = true; 283 IPC_BEGIN_MESSAGE_MAP(ClientSideDetectionHost, message) 284 IPC_MESSAGE_HANDLER(SafeBrowsingHostMsg_PhishingDetectionDone, 285 OnPhishingDetectionDone) 286 IPC_MESSAGE_UNHANDLED(handled = false) 287 IPC_END_MESSAGE_MAP() 288 return handled; 289 } 290 291 void ClientSideDetectionHost::DidNavigateMainFrame( 292 const content::LoadCommittedDetails& details, 293 const content::FrameNavigateParams& params) { 294 malware_or_phishing_match_ = false; 295 296 // TODO(noelutz): move this DCHECK to WebContents and fix all the unit tests 297 // that don't call this method on the UI thread. 298 // DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 299 if (details.is_in_page) { 300 // If the navigation is within the same page, the user isn't really 301 // navigating away. We don't need to cancel a pending callback or 302 // begin a new classification. 303 return; 304 } 305 // If we navigate away and there currently is a pending phishing 306 // report request we have to cancel it to make sure we don't display 307 // an interstitial for the wrong page. Note that this won't cancel 308 // the server ping back but only cancel the showing of the 309 // interstial. 310 weak_factory_.InvalidateWeakPtrs(); 311 312 if (!csd_service_) { 313 return; 314 } 315 316 // Cancel any pending classification request. 317 if (classification_request_.get()) { 318 classification_request_->Cancel(); 319 } 320 browse_info_.reset(new BrowseInfo); 321 322 // Store redirect chain information. 323 if (params.url.host() != cur_host_) { 324 cur_host_ = params.url.host(); 325 cur_host_redirects_ = params.redirects; 326 } 327 browse_info_->host_redirects = cur_host_redirects_; 328 browse_info_->url_redirects = params.redirects; 329 browse_info_->referrer = params.referrer.url; 330 browse_info_->http_status_code = details.http_status_code; 331 332 // Notify the renderer if it should classify this URL. 333 classification_request_ = new ShouldClassifyUrlRequest( 334 params, web_contents(), csd_service_, database_manager_.get(), this); 335 classification_request_->Start(); 336 } 337 338 void ClientSideDetectionHost::OnSafeBrowsingHit( 339 const SafeBrowsingUIManager::UnsafeResource& resource) { 340 if (!web_contents() || !web_contents()->GetController().GetActiveEntry()) 341 return; 342 343 // Check that the hit is either malware or phishing. 344 if (resource.threat_type != SB_THREAT_TYPE_URL_PHISHING && 345 resource.threat_type != SB_THREAT_TYPE_URL_MALWARE) 346 return; 347 348 // Check that this notification is really for us. 349 content::RenderViewHost* hit_rvh = content::RenderViewHost::FromID( 350 resource.render_process_host_id, resource.render_view_id); 351 if (!hit_rvh || 352 web_contents() != content::WebContents::FromRenderViewHost(hit_rvh)) 353 return; 354 355 // Store the unique page ID for later. 356 unsafe_unique_page_id_ = 357 web_contents()->GetController().GetActiveEntry()->GetUniqueID(); 358 // We also keep the resource around in order to be able to send the 359 // malicious URL to the server. 360 unsafe_resource_.reset(new SafeBrowsingUIManager::UnsafeResource(resource)); 361 unsafe_resource_->callback.Reset(); // Don't do anything stupid. 362 } 363 364 void ClientSideDetectionHost::OnSafeBrowsingMatch( 365 const SafeBrowsingUIManager::UnsafeResource& resource) { 366 malware_or_phishing_match_ = true; 367 } 368 369 scoped_refptr<SafeBrowsingDatabaseManager> 370 ClientSideDetectionHost::database_manager() { 371 return database_manager_; 372 } 373 374 bool ClientSideDetectionHost::DidPageReceiveSafeBrowsingMatch() const { 375 return malware_or_phishing_match_ || DidShowSBInterstitial(); 376 } 377 378 void ClientSideDetectionHost::WebContentsDestroyed(WebContents* tab) { 379 DCHECK(tab); 380 // Tell any pending classification request that it is being canceled. 381 if (classification_request_.get()) { 382 classification_request_->Cancel(); 383 } 384 // Cancel all pending feature extractions. 385 feature_extractor_.reset(); 386 } 387 388 void ClientSideDetectionHost::OnPhishingDetectionDone( 389 const std::string& verdict_str) { 390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 391 // There is something seriously wrong if there is no service class but 392 // this method is called. The renderer should not start phishing detection 393 // if there isn't any service class in the browser. 394 DCHECK(csd_service_); 395 // There shouldn't be any pending requests because we revoke them everytime 396 // we navigate away. 397 DCHECK(!weak_factory_.HasWeakPtrs()); 398 DCHECK(browse_info_.get()); 399 400 // We parse the protocol buffer here. If we're unable to parse it we won't 401 // send the verdict further. 402 scoped_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest); 403 if (csd_service_ && 404 !weak_factory_.HasWeakPtrs() && 405 browse_info_.get() && 406 verdict->ParseFromString(verdict_str) && 407 verdict->IsInitialized()) { 408 // We do the malware IP matching and request sending if the feature 409 // is enabled. 410 if (malware_report_enabled_ && !MalwareKillSwitchIsOn()) { 411 scoped_ptr<ClientMalwareRequest> malware_verdict( 412 new ClientMalwareRequest); 413 // Start browser-side malware feature extraction. Once we're done it will 414 // send the malware client verdict request. 415 malware_verdict->set_url(verdict->url()); 416 const GURL& referrer = browse_info_->referrer; 417 if (referrer.SchemeIs("http")) { // Only send http urls. 418 malware_verdict->set_referrer_url(referrer.spec()); 419 } 420 // This function doesn't expect browse_info_ to stay around after this 421 // function returns. 422 feature_extractor_->ExtractMalwareFeatures( 423 browse_info_.get(), 424 malware_verdict.release(), 425 base::Bind(&ClientSideDetectionHost::MalwareFeatureExtractionDone, 426 weak_factory_.GetWeakPtr())); 427 } 428 429 // We only send phishing verdict to the server if the verdict is phishing or 430 // if a SafeBrowsing interstitial was already shown for this site. E.g., a 431 // malware or phishing interstitial was shown but the user clicked 432 // through. 433 if (verdict->is_phishing() || DidShowSBInterstitial()) { 434 if (DidShowSBInterstitial()) { 435 browse_info_->unsafe_resource.reset(unsafe_resource_.release()); 436 } 437 // Start browser-side feature extraction. Once we're done it will send 438 // the client verdict request. 439 feature_extractor_->ExtractFeatures( 440 browse_info_.get(), 441 verdict.release(), 442 base::Bind(&ClientSideDetectionHost::FeatureExtractionDone, 443 weak_factory_.GetWeakPtr())); 444 } 445 } 446 browse_info_.reset(); 447 } 448 449 void ClientSideDetectionHost::MaybeShowPhishingWarning(GURL phishing_url, 450 bool is_phishing) { 451 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 452 VLOG(2) << "Received server phishing verdict for URL:" << phishing_url 453 << " is_phishing:" << is_phishing; 454 if (is_phishing) { 455 DCHECK(web_contents()); 456 if (ui_manager_.get()) { 457 SafeBrowsingUIManager::UnsafeResource resource; 458 resource.url = phishing_url; 459 resource.original_url = phishing_url; 460 resource.is_subresource = false; 461 resource.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL; 462 resource.render_process_host_id = 463 web_contents()->GetRenderProcessHost()->GetID(); 464 resource.render_view_id = 465 web_contents()->GetRenderViewHost()->GetRoutingID(); 466 if (!ui_manager_->IsWhitelisted(resource)) { 467 // We need to stop any pending navigations, otherwise the interstital 468 // might not get created properly. 469 web_contents()->GetController().DiscardNonCommittedEntries(); 470 } 471 ui_manager_->DoDisplayBlockingPage(resource); 472 } 473 // If there is true phishing verdict, invalidate weakptr so that no longer 474 // consider the malware vedict. 475 weak_factory_.InvalidateWeakPtrs(); 476 } 477 } 478 479 void ClientSideDetectionHost::MaybeShowMalwareWarning(GURL original_url, 480 GURL malware_url, 481 bool is_malware) { 482 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 483 VLOG(2) << "Received server malawre IP verdict for URL:" << malware_url 484 << " is_malware:" << is_malware; 485 if (is_malware && malware_url.is_valid() && original_url.is_valid()) { 486 DCHECK(web_contents()); 487 if (ui_manager_.get()) { 488 SafeBrowsingUIManager::UnsafeResource resource; 489 resource.url = malware_url; 490 resource.original_url = original_url; 491 resource.is_subresource = (malware_url.host() != original_url.host()); 492 resource.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL; 493 resource.render_process_host_id = 494 web_contents()->GetRenderProcessHost()->GetID(); 495 resource.render_view_id = 496 web_contents()->GetRenderViewHost()->GetRoutingID(); 497 if (!ui_manager_->IsWhitelisted(resource)) { 498 // We need to stop any pending navigations, otherwise the interstital 499 // might not get created properly. 500 web_contents()->GetController().DiscardNonCommittedEntries(); 501 } 502 ui_manager_->DoDisplayBlockingPage(resource); 503 } 504 // If there is true malware verdict, invalidate weakptr so that no longer 505 // consider the phishing vedict. 506 weak_factory_.InvalidateWeakPtrs(); 507 } 508 } 509 510 void ClientSideDetectionHost::FeatureExtractionDone( 511 bool success, 512 ClientPhishingRequest* request) { 513 DCHECK(request); 514 VLOG(2) << "Feature extraction done (success:" << success << ") for URL: " 515 << request->url() << ". Start sending client phishing request."; 516 ClientSideDetectionService::ClientReportPhishingRequestCallback callback; 517 // If the client-side verdict isn't phishing we don't care about the server 518 // response because we aren't going to display a warning. 519 if (request->is_phishing()) { 520 callback = base::Bind(&ClientSideDetectionHost::MaybeShowPhishingWarning, 521 weak_factory_.GetWeakPtr()); 522 } 523 // Send ping even if the browser feature extraction failed. 524 csd_service_->SendClientReportPhishingRequest( 525 request, // The service takes ownership of the request object. 526 callback); 527 } 528 529 void ClientSideDetectionHost::MalwareFeatureExtractionDone( 530 bool feature_extraction_success, 531 scoped_ptr<ClientMalwareRequest> request) { 532 DCHECK(request.get()); 533 VLOG(2) << "Malware Feature extraction done for URL: " << request->url() 534 << ", with badip url count:" << request->bad_ip_url_info_size(); 535 536 // Send ping if there is matching features. 537 if (feature_extraction_success && request->bad_ip_url_info_size() > 0) { 538 VLOG(1) << "Start sending client malware request."; 539 ClientSideDetectionService::ClientReportMalwareRequestCallback callback; 540 callback = base::Bind(&ClientSideDetectionHost::MaybeShowMalwareWarning, 541 weak_factory_.GetWeakPtr()); 542 csd_service_->SendClientReportMalwareRequest(request.release(), callback); 543 } 544 } 545 546 void ClientSideDetectionHost::UpdateIPUrlMap( 547 const std::string& ip, 548 const std::string& url, 549 const std::string& method, 550 const std::string& referrer, 551 const ResourceType::Type resource_type) { 552 if (ip.empty() || url.empty()) 553 return; 554 555 IPUrlMap::iterator it = browse_info_->ips.find(ip); 556 if (it == browse_info_->ips.end()) { 557 if (int(browse_info_->ips.size()) < kMaxIPsPerBrowse) { 558 std::vector<IPUrlInfo> url_infos; 559 url_infos.push_back(IPUrlInfo(url, method, referrer, resource_type)); 560 browse_info_->ips.insert(make_pair(ip, url_infos)); 561 } 562 } else if (int(it->second.size()) < kMaxUrlsPerIP) { 563 it->second.push_back(IPUrlInfo(url, method, referrer, resource_type)); 564 } 565 } 566 567 void ClientSideDetectionHost::Observe( 568 int type, 569 const content::NotificationSource& source, 570 const content::NotificationDetails& details) { 571 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 572 DCHECK_EQ(type, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED); 573 const ResourceRequestDetails* req = content::Details<ResourceRequestDetails>( 574 details).ptr(); 575 if (req && browse_info_.get() && malware_report_enabled_ && 576 !MalwareKillSwitchIsOn()) { 577 if (req->url.is_valid()) { 578 UpdateIPUrlMap(req->socket_address.host() /* ip */, 579 req->url.spec() /* url */, 580 req->method, 581 req->referrer, 582 req->resource_type); 583 } 584 } 585 } 586 587 bool ClientSideDetectionHost::DidShowSBInterstitial() const { 588 if (unsafe_unique_page_id_ <= 0 || !web_contents()) { 589 return false; 590 } 591 const NavigationEntry* nav_entry = 592 web_contents()->GetController().GetActiveEntry(); 593 return (nav_entry && nav_entry->GetUniqueID() == unsafe_unique_page_id_); 594 } 595 596 void ClientSideDetectionHost::set_client_side_detection_service( 597 ClientSideDetectionService* service) { 598 csd_service_ = service; 599 } 600 601 void ClientSideDetectionHost::set_safe_browsing_managers( 602 SafeBrowsingUIManager* ui_manager, 603 SafeBrowsingDatabaseManager* database_manager) { 604 if (ui_manager_.get()) 605 ui_manager_->RemoveObserver(this); 606 607 ui_manager_ = ui_manager; 608 if (ui_manager) 609 ui_manager_->AddObserver(this); 610 611 database_manager_ = database_manager; 612 } 613 614 bool ClientSideDetectionHost::MalwareKillSwitchIsOn() { 615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 616 return malware_killswitch_on_; 617 } 618 619 void ClientSideDetectionHost::SetMalwareKillSwitch(bool killswitch_on) { 620 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 621 malware_killswitch_on_ = killswitch_on; 622 } 623 624 } // namespace safe_browsing 625