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 "base/files/file_path.h" 6 #include "base/memory/ref_counted.h" 7 #include "base/memory/scoped_ptr.h" 8 #include "base/run_loop.h" 9 #include "base/strings/stringprintf.h" 10 #include "base/synchronization/waitable_event.h" 11 #include "chrome/browser/safe_browsing/browser_feature_extractor.h" 12 #include "chrome/browser/safe_browsing/client_side_detection_host.h" 13 #include "chrome/browser/safe_browsing/client_side_detection_service.h" 14 #include "chrome/browser/safe_browsing/database_manager.h" 15 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 16 #include "chrome/browser/safe_browsing/ui_manager.h" 17 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/safe_browsing/csd.pb.h" 19 #include "chrome/common/safe_browsing/safebrowsing_messages.h" 20 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 21 #include "chrome/test/base/testing_profile.h" 22 #include "content/public/browser/navigation_entry.h" 23 #include "content/public/browser/web_contents.h" 24 #include "content/public/test/mock_render_process_host.h" 25 #include "content/public/test/test_browser_thread.h" 26 #include "content/public/test/test_renderer_host.h" 27 #include "content/public/test/web_contents_tester.h" 28 #include "ipc/ipc_test_sink.h" 29 #include "testing/gmock/include/gmock/gmock.h" 30 #include "testing/gtest/include/gtest/gtest.h" 31 #include "url/gurl.h" 32 33 using ::testing::_; 34 using ::testing::DeleteArg; 35 using ::testing::DoAll; 36 using ::testing::Eq; 37 using ::testing::IsNull; 38 using ::testing::Mock; 39 using ::testing::NiceMock; 40 using ::testing::NotNull; 41 using ::testing::Pointee; 42 using ::testing::Return; 43 using ::testing::SaveArg; 44 using ::testing::SetArgumentPointee; 45 using ::testing::StrictMock; 46 using content::BrowserThread; 47 using content::RenderViewHostTester; 48 using content::ResourceType; 49 using content::WebContents; 50 51 namespace { 52 53 const bool kFalse = false; 54 const bool kTrue = true; 55 56 } // namespace 57 58 namespace safe_browsing { 59 namespace { 60 // This matcher verifies that the client computed verdict 61 // (ClientPhishingRequest) which is passed to SendClientReportPhishingRequest 62 // has the expected fields set. Note: we can't simply compare the protocol 63 // buffer strings because the BrowserFeatureExtractor might add features to the 64 // verdict object before calling SendClientReportPhishingRequest. 65 MATCHER_P(PartiallyEqualVerdict, other, "") { 66 return (other.url() == arg.url() && 67 other.client_score() == arg.client_score() && 68 other.is_phishing() == arg.is_phishing()); 69 } 70 71 MATCHER_P(PartiallyEqualMalwareVerdict, other, "") { 72 if (other.url() != arg.url() || 73 other.referrer_url() != arg.referrer_url() || 74 other.bad_ip_url_info_size() != arg.bad_ip_url_info_size()) 75 return false; 76 77 for (int i = 0; i < other.bad_ip_url_info_size(); ++i) { 78 if (other.bad_ip_url_info(i).ip() != arg.bad_ip_url_info(i).ip() || 79 other.bad_ip_url_info(i).url() != arg.bad_ip_url_info(i).url()) 80 return false; 81 } 82 return true; 83 } 84 85 // Test that the callback is NULL when the verdict is not phishing. 86 MATCHER(CallbackIsNull, "") { 87 return arg.is_null(); 88 } 89 90 ACTION(QuitUIMessageLoop) { 91 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 92 base::MessageLoopForUI::current()->Quit(); 93 } 94 95 ACTION_P(InvokeDoneCallback, verdict) { 96 scoped_ptr<ClientPhishingRequest> request(::std::tr1::get<1>(args)); 97 request->CopyFrom(*verdict); 98 ::std::tr1::get<2>(args).Run(true, request.Pass()); 99 } 100 101 ACTION_P(InvokeMalwareCallback, verdict) { 102 scoped_ptr<ClientMalwareRequest> request(::std::tr1::get<1>(args)); 103 request->CopyFrom(*verdict); 104 ::std::tr1::get<2>(args).Run(true, request.Pass()); 105 } 106 107 void EmptyUrlCheckCallback(bool processed) { 108 } 109 110 class MockClientSideDetectionService : public ClientSideDetectionService { 111 public: 112 MockClientSideDetectionService() : ClientSideDetectionService(NULL) {} 113 virtual ~MockClientSideDetectionService() {} 114 115 MOCK_METHOD2(SendClientReportPhishingRequest, 116 void(ClientPhishingRequest*, 117 const ClientReportPhishingRequestCallback&)); 118 MOCK_METHOD2(SendClientReportMalwareRequest, 119 void(ClientMalwareRequest*, 120 const ClientReportMalwareRequestCallback&)); 121 MOCK_CONST_METHOD1(IsPrivateIPAddress, bool(const std::string&)); 122 MOCK_METHOD2(GetValidCachedResult, bool(const GURL&, bool*)); 123 MOCK_METHOD1(IsInCache, bool(const GURL&)); 124 MOCK_METHOD0(OverPhishingReportLimit, bool()); 125 MOCK_METHOD0(OverMalwareReportLimit, bool()); 126 127 private: 128 DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService); 129 }; 130 131 class MockSafeBrowsingUIManager : public SafeBrowsingUIManager { 132 public: 133 explicit MockSafeBrowsingUIManager(SafeBrowsingService* service) 134 : SafeBrowsingUIManager(service) { } 135 136 MOCK_METHOD1(DisplayBlockingPage, void(const UnsafeResource& resource)); 137 138 // Helper function which calls OnBlockingPageComplete for this client 139 // object. 140 void InvokeOnBlockingPageComplete(const UrlCheckCallback& callback) { 141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 142 // Note: this will delete the client object in the case of the CsdClient 143 // implementation. 144 if (!callback.is_null()) 145 callback.Run(false); 146 } 147 148 protected: 149 virtual ~MockSafeBrowsingUIManager() { } 150 151 private: 152 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager); 153 }; 154 155 class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager { 156 public: 157 explicit MockSafeBrowsingDatabaseManager(SafeBrowsingService* service) 158 : SafeBrowsingDatabaseManager(service) { } 159 160 MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&)); 161 MOCK_METHOD1(MatchMalwareIP, bool(const std::string& ip_address)); 162 MOCK_METHOD0(IsMalwareKillSwitchOn, bool()); 163 164 protected: 165 virtual ~MockSafeBrowsingDatabaseManager() {} 166 167 private: 168 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager); 169 }; 170 171 class MockTestingProfile : public TestingProfile { 172 public: 173 MockTestingProfile() {} 174 virtual ~MockTestingProfile() {} 175 176 MOCK_CONST_METHOD0(IsOffTheRecord, bool()); 177 }; 178 179 class MockBrowserFeatureExtractor : public BrowserFeatureExtractor { 180 public: 181 explicit MockBrowserFeatureExtractor( 182 WebContents* tab, 183 ClientSideDetectionHost* host) 184 : BrowserFeatureExtractor(tab, host) {} 185 virtual ~MockBrowserFeatureExtractor() {} 186 187 MOCK_METHOD3(ExtractFeatures, 188 void(const BrowseInfo*, 189 ClientPhishingRequest*, 190 const BrowserFeatureExtractor::DoneCallback&)); 191 192 MOCK_METHOD3(ExtractMalwareFeatures, 193 void(BrowseInfo*, 194 ClientMalwareRequest*, 195 const BrowserFeatureExtractor::MalwareDoneCallback&)); 196 }; 197 198 } // namespace 199 200 class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness { 201 public: 202 typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource; 203 204 virtual void SetUp() { 205 ChromeRenderViewHostTestHarness::SetUp(); 206 207 // Inject service classes. 208 csd_service_.reset(new StrictMock<MockClientSideDetectionService>()); 209 // Only used for initializing mock objects. 210 SafeBrowsingService* sb_service = 211 SafeBrowsingService::CreateSafeBrowsingService(); 212 database_manager_ = 213 new StrictMock<MockSafeBrowsingDatabaseManager>(sb_service); 214 ui_manager_ = new StrictMock<MockSafeBrowsingUIManager>(sb_service); 215 csd_host_.reset(safe_browsing::ClientSideDetectionHost::Create( 216 web_contents())); 217 csd_host_->set_client_side_detection_service(csd_service_.get()); 218 csd_host_->set_safe_browsing_managers(ui_manager_.get(), 219 database_manager_.get()); 220 // We need to create this here since we don't call DidStopLanding in 221 // this test. 222 csd_host_->browse_info_.reset(new BrowseInfo); 223 } 224 225 virtual void TearDown() { 226 // Delete the host object on the UI thread and release the 227 // SafeBrowsingService. 228 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, 229 csd_host_.release()); 230 database_manager_ = NULL; 231 ui_manager_ = NULL; 232 base::RunLoop().RunUntilIdle(); 233 ChromeRenderViewHostTestHarness::TearDown(); 234 } 235 236 virtual content::BrowserContext* CreateBrowserContext() OVERRIDE { 237 // Set custom profile object so that we can mock calls to IsOffTheRecord. 238 // This needs to happen before we call the parent SetUp() function. We use 239 // a nice mock because other parts of the code are calling IsOffTheRecord. 240 mock_profile_ = new NiceMock<MockTestingProfile>(); 241 return mock_profile_; 242 } 243 244 void OnPhishingDetectionDone(const std::string& verdict_str) { 245 csd_host_->OnPhishingDetectionDone(verdict_str); 246 } 247 248 void DidStopLoading() { 249 csd_host_->DidStopLoading(pending_rvh()); 250 } 251 252 void UpdateIPUrlMap(const std::string& ip, const std::string& host) { 253 csd_host_->UpdateIPUrlMap(ip, host, "", "", content::RESOURCE_TYPE_OBJECT); 254 } 255 256 BrowseInfo* GetBrowseInfo() { 257 return csd_host_->browse_info_.get(); 258 } 259 260 void ExpectPreClassificationChecks(const GURL& url, 261 const bool* is_private, 262 const bool* is_incognito, 263 const bool* match_csd_whitelist, 264 const bool* malware_killswitch, 265 const bool* get_valid_cached_result, 266 const bool* is_in_cache, 267 const bool* over_phishing_report_limit, 268 const bool* over_malware_report_limit) { 269 if (is_private) { 270 EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)) 271 .WillOnce(Return(*is_private)); 272 } 273 if (is_incognito) { 274 EXPECT_CALL(*mock_profile_, IsOffTheRecord()) 275 .WillRepeatedly(Return(*is_incognito)); 276 } 277 if (match_csd_whitelist) { 278 EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(url)) 279 .WillOnce(Return(*match_csd_whitelist)); 280 } 281 if (malware_killswitch) { 282 EXPECT_CALL(*database_manager_.get(), IsMalwareKillSwitchOn()) 283 .WillRepeatedly(Return(*malware_killswitch)); 284 } 285 if (get_valid_cached_result) { 286 EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull())) 287 .WillOnce(DoAll(SetArgumentPointee<1>(true), 288 Return(*get_valid_cached_result))); 289 } 290 if (is_in_cache) { 291 EXPECT_CALL(*csd_service_, IsInCache(url)).WillOnce(Return(*is_in_cache)); 292 } 293 if (over_phishing_report_limit) { 294 EXPECT_CALL(*csd_service_, OverPhishingReportLimit()) 295 .WillOnce(Return(*over_phishing_report_limit)); 296 } 297 if (over_malware_report_limit) { 298 EXPECT_CALL(*csd_service_, OverMalwareReportLimit()) 299 .WillOnce(Return(*over_malware_report_limit)); 300 } 301 } 302 303 void WaitAndCheckPreClassificationChecks() { 304 // Wait for CheckCsdWhitelist and CheckCache() to be called if at all. 305 base::RunLoop().RunUntilIdle(); 306 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); 307 EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get())); 308 EXPECT_TRUE(Mock::VerifyAndClear(database_manager_.get())); 309 EXPECT_TRUE(Mock::VerifyAndClear(mock_profile_)); 310 } 311 312 void SetFeatureExtractor(BrowserFeatureExtractor* extractor) { 313 csd_host_->feature_extractor_.reset(extractor); 314 } 315 316 void SetRedirectChain(const std::vector<GURL>& redirect_chain) { 317 csd_host_->browse_info_->url_redirects = redirect_chain; 318 } 319 320 void SetReferrer(const GURL& referrer) { 321 csd_host_->browse_info_->referrer = referrer; 322 } 323 324 void ExpectShouldClassifyForMalwareResult(bool should_classify) { 325 EXPECT_EQ(should_classify, csd_host_->should_classify_for_malware_); 326 } 327 328 void ExpectStartPhishingDetection(const GURL* url) { 329 const IPC::Message* msg = process()->sink().GetFirstMessageMatching( 330 SafeBrowsingMsg_StartPhishingDetection::ID); 331 if (url) { 332 ASSERT_TRUE(msg); 333 Tuple1<GURL> actual_url; 334 SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url); 335 EXPECT_EQ(*url, actual_url.a); 336 EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id()); 337 process()->sink().ClearMessages(); 338 } else { 339 ASSERT_FALSE(msg); 340 } 341 } 342 343 void TestUnsafeResourceCopied(const UnsafeResource& resource) { 344 ASSERT_TRUE(csd_host_->unsafe_resource_.get()); 345 // Test that the resource from OnSafeBrowsingHit notification was copied 346 // into the CSDH. 347 EXPECT_EQ(resource.url, csd_host_->unsafe_resource_->url); 348 EXPECT_EQ(resource.original_url, csd_host_->unsafe_resource_->original_url); 349 EXPECT_EQ(resource.is_subresource, 350 csd_host_->unsafe_resource_->is_subresource); 351 EXPECT_EQ(resource.threat_type, csd_host_->unsafe_resource_->threat_type); 352 EXPECT_TRUE(csd_host_->unsafe_resource_->callback.is_null()); 353 EXPECT_EQ(resource.render_process_host_id, 354 csd_host_->unsafe_resource_->render_process_host_id); 355 EXPECT_EQ(resource.render_view_id, 356 csd_host_->unsafe_resource_->render_view_id); 357 } 358 359 void SetUnsafeSubResourceForCurrent() { 360 UnsafeResource resource; 361 resource.url = GURL("http://www.malware.com/"); 362 resource.original_url = web_contents()->GetURL(); 363 resource.is_subresource = true; 364 resource.threat_type = SB_THREAT_TYPE_URL_MALWARE; 365 resource.callback = base::Bind(&EmptyUrlCheckCallback); 366 resource.render_process_host_id = web_contents()->GetRenderProcessHost()-> 367 GetID(); 368 resource.render_view_id = 369 web_contents()->GetRenderViewHost()->GetRoutingID(); 370 ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 371 csd_host_->OnSafeBrowsingMatch(resource); 372 ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 373 csd_host_->OnSafeBrowsingHit(resource); 374 ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 375 resource.callback.Reset(); 376 ASSERT_TRUE(csd_host_->DidShowSBInterstitial()); 377 TestUnsafeResourceCopied(resource); 378 } 379 380 void NavigateWithSBHitAndCommit(const GURL& url) { 381 // Create a pending navigation. 382 controller().LoadURL( 383 url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); 384 385 ASSERT_TRUE(pending_rvh()); 386 if (web_contents()->GetRenderViewHost()->GetProcess()->GetID() == 387 pending_rvh()->GetProcess()->GetID()) { 388 EXPECT_NE(web_contents()->GetRenderViewHost()->GetRoutingID(), 389 pending_rvh()->GetRoutingID()); 390 } 391 392 // Simulate a safebrowsing hit before navigation completes. 393 UnsafeResource resource; 394 resource.url = url; 395 resource.original_url = url; 396 resource.is_subresource = false; 397 resource.threat_type = SB_THREAT_TYPE_URL_MALWARE; 398 resource.callback = base::Bind(&EmptyUrlCheckCallback); 399 resource.render_process_host_id = pending_rvh()->GetProcess()->GetID(); 400 resource.render_view_id = pending_rvh()->GetRoutingID(); 401 csd_host_->OnSafeBrowsingMatch(resource); 402 csd_host_->OnSafeBrowsingHit(resource); 403 resource.callback.Reset(); 404 405 ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 406 407 // LoadURL created a navigation entry, now simulate the RenderView sending 408 // a notification that it actually navigated. 409 content::WebContentsTester::For(web_contents())->CommitPendingNavigation(); 410 411 ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 412 ASSERT_TRUE(csd_host_->DidShowSBInterstitial()); 413 TestUnsafeResourceCopied(resource); 414 } 415 416 void NavigateWithoutSBHitAndCommit(const GURL& safe_url) { 417 controller().LoadURL( 418 safe_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, 419 std::string()); 420 421 ASSERT_TRUE(pending_rvh()); 422 if (web_contents()->GetRenderViewHost()->GetProcess()->GetID() == 423 pending_rvh()->GetProcess()->GetID()) { 424 EXPECT_NE(web_contents()->GetRenderViewHost()->GetRoutingID(), 425 pending_rvh()->GetRoutingID()); 426 } 427 ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 428 ASSERT_FALSE(csd_host_->DidShowSBInterstitial()); 429 430 content::WebContentsTester::For(web_contents())->CommitPendingNavigation(); 431 ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch()); 432 ASSERT_FALSE(csd_host_->DidShowSBInterstitial()); 433 } 434 435 void CheckIPUrlEqual(const std::vector<IPUrlInfo>& expect, 436 const std::vector<IPUrlInfo>& result) { 437 ASSERT_EQ(expect.size(), result.size()); 438 439 for (unsigned int i = 0; i < expect.size(); ++i) { 440 EXPECT_EQ(expect[i].url, result[i].url); 441 EXPECT_EQ(expect[i].method, result[i].method); 442 EXPECT_EQ(expect[i].referrer, result[i].referrer); 443 EXPECT_EQ(expect[i].resource_type, result[i].resource_type); 444 } 445 } 446 447 protected: 448 scoped_ptr<ClientSideDetectionHost> csd_host_; 449 scoped_ptr<StrictMock<MockClientSideDetectionService> > csd_service_; 450 scoped_refptr<StrictMock<MockSafeBrowsingUIManager> > ui_manager_; 451 scoped_refptr<StrictMock<MockSafeBrowsingDatabaseManager> > database_manager_; 452 MockTestingProfile* mock_profile_; // We don't own this object 453 }; 454 455 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneInvalidVerdict) { 456 // Case 0: renderer sends an invalid verdict string that we're unable to 457 // parse. 458 MockBrowserFeatureExtractor* mock_extractor = 459 new StrictMock<MockBrowserFeatureExtractor>( 460 web_contents(), 461 csd_host_.get()); 462 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 463 EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0); 464 OnPhishingDetectionDone("Invalid Protocol Buffer"); 465 EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor)); 466 } 467 468 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneNotPhishing) { 469 // Case 1: client thinks the page is phishing. The server does not agree. 470 // No interstitial is shown. 471 MockBrowserFeatureExtractor* mock_extractor = 472 new StrictMock<MockBrowserFeatureExtractor>( 473 web_contents(), 474 csd_host_.get()); 475 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 476 477 ClientSideDetectionService::ClientReportPhishingRequestCallback cb; 478 ClientPhishingRequest verdict; 479 verdict.set_url("http://phishingurl.com/"); 480 verdict.set_client_score(1.0f); 481 verdict.set_is_phishing(true); 482 483 EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)) 484 .WillOnce(InvokeDoneCallback(&verdict)); 485 EXPECT_CALL(*csd_service_, 486 SendClientReportPhishingRequest( 487 Pointee(PartiallyEqualVerdict(verdict)), _)) 488 .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb))); 489 OnPhishingDetectionDone(verdict.SerializeAsString()); 490 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 491 ASSERT_FALSE(cb.is_null()); 492 493 // Make sure DisplayBlockingPage is not going to be called. 494 EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0); 495 cb.Run(GURL(verdict.url()), false); 496 base::RunLoop().RunUntilIdle(); 497 EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get())); 498 } 499 500 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneDisabled) { 501 // Case 2: client thinks the page is phishing and so does the server but 502 // showing the interstitial is disabled => no interstitial is shown. 503 MockBrowserFeatureExtractor* mock_extractor = 504 new StrictMock<MockBrowserFeatureExtractor>( 505 web_contents(), 506 csd_host_.get()); 507 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 508 509 ClientSideDetectionService::ClientReportPhishingRequestCallback cb; 510 ClientPhishingRequest verdict; 511 verdict.set_url("http://phishingurl.com/"); 512 verdict.set_client_score(1.0f); 513 verdict.set_is_phishing(true); 514 515 EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)) 516 .WillOnce(InvokeDoneCallback(&verdict)); 517 EXPECT_CALL(*csd_service_, 518 SendClientReportPhishingRequest( 519 Pointee(PartiallyEqualVerdict(verdict)), _)) 520 .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb))); 521 OnPhishingDetectionDone(verdict.SerializeAsString()); 522 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 523 ASSERT_FALSE(cb.is_null()); 524 525 // Make sure DisplayBlockingPage is not going to be called. 526 EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0); 527 cb.Run(GURL(verdict.url()), false); 528 base::RunLoop().RunUntilIdle(); 529 EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get())); 530 } 531 532 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneShowInterstitial) { 533 // Case 3: client thinks the page is phishing and so does the server. 534 // We show an interstitial. 535 MockBrowserFeatureExtractor* mock_extractor = 536 new StrictMock<MockBrowserFeatureExtractor>( 537 web_contents(), 538 csd_host_.get()); 539 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 540 541 ClientSideDetectionService::ClientReportPhishingRequestCallback cb; 542 GURL phishing_url("http://phishingurl.com/"); 543 ClientPhishingRequest verdict; 544 verdict.set_url(phishing_url.spec()); 545 verdict.set_client_score(1.0f); 546 verdict.set_is_phishing(true); 547 548 EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)) 549 .WillOnce(InvokeDoneCallback(&verdict)); 550 EXPECT_CALL(*csd_service_, 551 SendClientReportPhishingRequest( 552 Pointee(PartiallyEqualVerdict(verdict)), _)) 553 .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb))); 554 OnPhishingDetectionDone(verdict.SerializeAsString()); 555 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 556 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); 557 ASSERT_FALSE(cb.is_null()); 558 559 UnsafeResource resource; 560 EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)) 561 .WillOnce(SaveArg<0>(&resource)); 562 cb.Run(phishing_url, true); 563 564 base::RunLoop().RunUntilIdle(); 565 EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get())); 566 EXPECT_EQ(phishing_url, resource.url); 567 EXPECT_EQ(phishing_url, resource.original_url); 568 EXPECT_FALSE(resource.is_subresource); 569 EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, resource.threat_type); 570 EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(), 571 resource.render_process_host_id); 572 EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(), 573 resource.render_view_id); 574 575 // Make sure the client object will be deleted. 576 BrowserThread::PostTask( 577 BrowserThread::IO, 578 FROM_HERE, 579 base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete, 580 ui_manager_, resource.callback)); 581 } 582 583 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneMultiplePings) { 584 // Case 4 & 5: client thinks a page is phishing then navigates to 585 // another page which is also considered phishing by the client 586 // before the server responds with a verdict. After a while the 587 // server responds for both requests with a phishing verdict. Only 588 // a single interstitial is shown for the second URL. 589 MockBrowserFeatureExtractor* mock_extractor = 590 new StrictMock<MockBrowserFeatureExtractor>( 591 web_contents(), 592 csd_host_.get()); 593 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 594 595 ClientSideDetectionService::ClientReportPhishingRequestCallback cb; 596 GURL phishing_url("http://phishingurl.com/"); 597 ClientPhishingRequest verdict; 598 verdict.set_url(phishing_url.spec()); 599 verdict.set_client_score(1.0f); 600 verdict.set_is_phishing(true); 601 602 EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)) 603 .WillOnce(InvokeDoneCallback(&verdict)); 604 EXPECT_CALL(*csd_service_, 605 SendClientReportPhishingRequest( 606 Pointee(PartiallyEqualVerdict(verdict)), _)) 607 .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb))); 608 OnPhishingDetectionDone(verdict.SerializeAsString()); 609 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 610 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); 611 ASSERT_FALSE(cb.is_null()); 612 613 // Set this back to a normal browser feature extractor since we're using 614 // NavigateAndCommit() and it's easier to use the real thing than setting up 615 // mock expectations. 616 SetFeatureExtractor(new BrowserFeatureExtractor(web_contents(), 617 csd_host_.get())); 618 GURL other_phishing_url("http://other_phishing_url.com/bla"); 619 ExpectPreClassificationChecks(other_phishing_url, &kFalse, &kFalse, &kFalse, 620 &kFalse, &kFalse, &kFalse, &kFalse, &kFalse); 621 // We navigate away. The callback cb should be revoked. 622 NavigateAndCommit(other_phishing_url); 623 // Wait for the pre-classification checks to finish for other_phishing_url. 624 WaitAndCheckPreClassificationChecks(); 625 626 ClientSideDetectionService::ClientReportPhishingRequestCallback cb_other; 627 verdict.set_url(other_phishing_url.spec()); 628 verdict.set_client_score(0.8f); 629 EXPECT_CALL(*csd_service_, 630 SendClientReportPhishingRequest( 631 Pointee(PartiallyEqualVerdict(verdict)), _)) 632 .WillOnce(DoAll(DeleteArg<0>(), 633 SaveArg<1>(&cb_other), 634 QuitUIMessageLoop())); 635 std::vector<GURL> redirect_chain; 636 redirect_chain.push_back(other_phishing_url); 637 SetRedirectChain(redirect_chain); 638 OnPhishingDetectionDone(verdict.SerializeAsString()); 639 base::MessageLoop::current()->Run(); 640 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 641 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); 642 ASSERT_FALSE(cb_other.is_null()); 643 644 // We expect that the interstitial is shown for the second phishing URL and 645 // not for the first phishing URL. 646 UnsafeResource resource; 647 EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)) 648 .WillOnce(SaveArg<0>(&resource)); 649 650 cb.Run(phishing_url, true); // Should have no effect. 651 cb_other.Run(other_phishing_url, true); // Should show interstitial. 652 653 base::RunLoop().RunUntilIdle(); 654 EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get())); 655 EXPECT_EQ(other_phishing_url, resource.url); 656 EXPECT_EQ(other_phishing_url, resource.original_url); 657 EXPECT_FALSE(resource.is_subresource); 658 EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, resource.threat_type); 659 EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(), 660 resource.render_process_host_id); 661 EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(), 662 resource.render_view_id); 663 664 // Make sure the client object will be deleted. 665 BrowserThread::PostTask( 666 BrowserThread::IO, 667 FROM_HERE, 668 base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete, 669 ui_manager_, resource.callback)); 670 } 671 672 TEST_F(ClientSideDetectionHostTest, 673 OnPhishingDetectionDoneVerdictNotPhishing) { 674 // Case 6: renderer sends a verdict string that isn't phishing. 675 MockBrowserFeatureExtractor* mock_extractor = 676 new StrictMock<MockBrowserFeatureExtractor>( 677 web_contents(), 678 csd_host_.get()); 679 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 680 681 ClientPhishingRequest verdict; 682 verdict.set_url("http://not-phishing.com/"); 683 verdict.set_client_score(0.1f); 684 verdict.set_is_phishing(false); 685 686 EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0); 687 OnPhishingDetectionDone(verdict.SerializeAsString()); 688 EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor)); 689 } 690 691 TEST_F(ClientSideDetectionHostTest, 692 OnPhishingDetectionDoneVerdictNotPhishingButSBMatchSubResource) { 693 // Case 7: renderer sends a verdict string that isn't phishing but the URL 694 // of a subresource was on the regular phishing or malware lists. 695 GURL url("http://not-phishing.com/"); 696 ClientPhishingRequest verdict; 697 verdict.set_url(url.spec()); 698 verdict.set_client_score(0.1f); 699 verdict.set_is_phishing(false); 700 701 // First we have to navigate to the URL to set the unique page ID. 702 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 703 &kFalse, &kFalse, &kFalse, &kFalse); 704 NavigateAndCommit(url); 705 WaitAndCheckPreClassificationChecks(); 706 SetUnsafeSubResourceForCurrent(); 707 708 EXPECT_CALL(*csd_service_, 709 SendClientReportPhishingRequest( 710 Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull())) 711 .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop())); 712 std::vector<GURL> redirect_chain; 713 redirect_chain.push_back(url); 714 SetRedirectChain(redirect_chain); 715 OnPhishingDetectionDone(verdict.SerializeAsString()); 716 base::MessageLoop::current()->Run(); 717 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 718 } 719 720 TEST_F(ClientSideDetectionHostTest, 721 OnPhishingDetectionDoneVerdictNotPhishingButSBMatchOnNewRVH) { 722 // When navigating to a different host (thus creating a pending RVH) which 723 // matches regular malware list, and after navigation the renderer sends a 724 // verdict string that isn't phishing, we should still send the report. 725 726 // Do an initial navigation to a safe host. 727 GURL start_url("http://safe.example.com/"); 728 ExpectPreClassificationChecks( 729 start_url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, 730 &kFalse); 731 NavigateAndCommit(start_url); 732 WaitAndCheckPreClassificationChecks(); 733 734 // Now navigate to a different host which will have a malware hit before the 735 // navigation commits. 736 GURL url("http://malware-but-not-phishing.com/"); 737 ClientPhishingRequest verdict; 738 verdict.set_url(url.spec()); 739 verdict.set_client_score(0.1f); 740 verdict.set_is_phishing(false); 741 742 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 743 &kFalse, &kFalse, &kFalse, &kFalse); 744 NavigateWithSBHitAndCommit(url); 745 WaitAndCheckPreClassificationChecks(); 746 747 EXPECT_CALL(*csd_service_, 748 SendClientReportPhishingRequest( 749 Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull())) 750 .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop())); 751 std::vector<GURL> redirect_chain; 752 redirect_chain.push_back(url); 753 SetRedirectChain(redirect_chain); 754 OnPhishingDetectionDone(verdict.SerializeAsString()); 755 base::MessageLoop::current()->Run(); 756 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 757 758 ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse, 759 &kFalse, &kFalse, &kFalse, &kFalse); 760 NavigateWithoutSBHitAndCommit(start_url); 761 WaitAndCheckPreClassificationChecks(); 762 } 763 764 TEST_F(ClientSideDetectionHostTest, 765 DidStopLoadingShowMalwareInterstitial) { 766 // Case 9: client thinks the page match malware IP and so does the server. 767 // We show an sub-resource malware interstitial. 768 MockBrowserFeatureExtractor* mock_extractor = 769 new StrictMock<MockBrowserFeatureExtractor>( 770 web_contents(), 771 csd_host_.get()); 772 SetFeatureExtractor(mock_extractor); // The host class takes ownership. 773 774 GURL malware_landing_url("http://malware.com/"); 775 GURL malware_ip_url("http://badip.com"); 776 ClientMalwareRequest malware_verdict; 777 malware_verdict.set_url("http://malware.com/"); 778 ClientMalwareRequest::UrlInfo* badipurl = 779 malware_verdict.add_bad_ip_url_info(); 780 badipurl->set_ip("1.2.3.4"); 781 badipurl->set_url("http://badip.com"); 782 783 ExpectPreClassificationChecks(GURL(malware_verdict.url()), &kFalse, &kFalse, 784 &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, 785 &kFalse); 786 NavigateAndCommit(GURL(malware_verdict.url())); 787 WaitAndCheckPreClassificationChecks(); 788 789 ClientSideDetectionService::ClientReportMalwareRequestCallback cb; 790 EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _)) 791 .WillOnce(InvokeMalwareCallback(&malware_verdict)); 792 EXPECT_CALL(*csd_service_, 793 SendClientReportMalwareRequest( 794 Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _)) 795 .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb))); 796 DidStopLoading(); 797 EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get())); 798 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); 799 ASSERT_FALSE(cb.is_null()); 800 801 UnsafeResource resource; 802 EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)) 803 .WillOnce(SaveArg<0>(&resource)); 804 cb.Run(malware_landing_url, malware_ip_url, true); 805 806 base::RunLoop().RunUntilIdle(); 807 EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get())); 808 EXPECT_EQ(malware_ip_url, resource.url); 809 EXPECT_EQ(malware_landing_url, resource.original_url); 810 EXPECT_TRUE(resource.is_subresource); 811 EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, resource.threat_type); 812 EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(), 813 resource.render_process_host_id); 814 EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(), 815 resource.render_view_id); 816 817 // Make sure the client object will be deleted. 818 BrowserThread::PostTask( 819 BrowserThread::IO, 820 FROM_HERE, 821 base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete, 822 ui_manager_, resource.callback)); 823 } 824 825 TEST_F(ClientSideDetectionHostTest, UpdateIPUrlMap) { 826 BrowseInfo* browse_info = GetBrowseInfo(); 827 828 // Empty IP or host are skipped 829 UpdateIPUrlMap("250.10.10.10", std::string()); 830 ASSERT_EQ(0U, browse_info->ips.size()); 831 UpdateIPUrlMap(std::string(), "http://google.com/a"); 832 ASSERT_EQ(0U, browse_info->ips.size()); 833 UpdateIPUrlMap(std::string(), std::string()); 834 ASSERT_EQ(0U, browse_info->ips.size()); 835 836 std::vector<IPUrlInfo> expected_urls; 837 for (int i = 0; i < 20; i++) { 838 std::string url = base::StringPrintf("http://%d.com/", i); 839 expected_urls.push_back( 840 IPUrlInfo(url, "", "", content::RESOURCE_TYPE_OBJECT)); 841 UpdateIPUrlMap("250.10.10.10", url); 842 } 843 ASSERT_EQ(1U, browse_info->ips.size()); 844 ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size()); 845 CheckIPUrlEqual(expected_urls, 846 browse_info->ips["250.10.10.10"]); 847 848 // Add more urls for this ip, it exceeds max limit and won't be added 849 UpdateIPUrlMap("250.10.10.10", "http://21.com/"); 850 ASSERT_EQ(1U, browse_info->ips.size()); 851 ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size()); 852 CheckIPUrlEqual(expected_urls, 853 browse_info->ips["250.10.10.10"]); 854 855 // Add 199 more IPs 856 for (int i = 0; i < 199; i++) { 857 std::string ip = base::StringPrintf("%d.%d.%d.256", i, i, i); 858 expected_urls.clear(); 859 expected_urls.push_back( 860 IPUrlInfo("test.com/", "", "", content::RESOURCE_TYPE_OBJECT)); 861 UpdateIPUrlMap(ip, "test.com/"); 862 ASSERT_EQ(1U, browse_info->ips[ip].size()); 863 CheckIPUrlEqual(expected_urls, 864 browse_info->ips[ip]); 865 } 866 ASSERT_EQ(200U, browse_info->ips.size()); 867 868 // Exceeding max ip limit 200, these won't be added 869 UpdateIPUrlMap("250.250.250.250", "goo.com/"); 870 UpdateIPUrlMap("250.250.250.250", "bar.com/"); 871 UpdateIPUrlMap("250.250.0.250", "foo.com/"); 872 ASSERT_EQ(200U, browse_info->ips.size()); 873 874 // Add url to existing IPs succeed 875 UpdateIPUrlMap("100.100.100.256", "more.com/"); 876 ASSERT_EQ(2U, browse_info->ips["100.100.100.256"].size()); 877 expected_urls.clear(); 878 expected_urls.push_back( 879 IPUrlInfo("test.com/", "", "", content::RESOURCE_TYPE_OBJECT)); 880 expected_urls.push_back( 881 IPUrlInfo("more.com/", "", "", content::RESOURCE_TYPE_OBJECT)); 882 CheckIPUrlEqual(expected_urls, 883 browse_info->ips["100.100.100.256"]); 884 } 885 886 TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) { 887 // Test that canceling pending should classify requests works as expected. 888 889 GURL first_url("http://first.phishy.url.com"); 890 GURL second_url("http://second.url.com/"); 891 // The first few checks are done synchronously so check that they have been 892 // done for the first URL, while the second URL has all the checks done. We 893 // need to manually set up the IsPrivateIPAddress mock since if the same mock 894 // expectation is specified twice, gmock will only use the last instance of 895 // it, meaning the first will never be matched. 896 EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)) 897 .WillOnce(Return(false)) 898 .WillOnce(Return(false)); 899 ExpectPreClassificationChecks(first_url, NULL, &kFalse, &kFalse, &kFalse, 900 NULL, NULL, NULL, NULL); 901 ExpectPreClassificationChecks(second_url, NULL, &kFalse, &kFalse, &kFalse, 902 &kFalse, &kFalse, &kFalse, &kFalse); 903 904 NavigateAndCommit(first_url); 905 // Don't flush the message loop, as we want to navigate to a different 906 // url before the final pre-classification checks are run. 907 NavigateAndCommit(second_url); 908 WaitAndCheckPreClassificationChecks(); 909 } 910 911 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckPass) { 912 // Navigate the tab to a page. We should see a StartPhishingDetection IPC. 913 GURL url("http://host.com/"); 914 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 915 &kFalse, &kFalse, &kFalse, &kFalse); 916 NavigateAndCommit(url); 917 WaitAndCheckPreClassificationChecks(); 918 919 ExpectStartPhishingDetection(&url); 920 ExpectShouldClassifyForMalwareResult(true); 921 } 922 923 TEST_F(ClientSideDetectionHostTest, 924 TestPreClassificationCheckInPageNavigation) { 925 GURL url("http://host.com/"); 926 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 927 &kFalse, &kFalse, &kFalse, &kFalse); 928 NavigateAndCommit(url); 929 WaitAndCheckPreClassificationChecks(); 930 931 ExpectStartPhishingDetection(&url); 932 ExpectShouldClassifyForMalwareResult(true); 933 934 // Now try an in-page navigation. This should not trigger an IPC. 935 EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0); 936 GURL inpage("http://host.com/#foo"); 937 ExpectPreClassificationChecks(inpage, NULL, NULL, NULL, NULL, NULL, NULL, 938 NULL, NULL); 939 NavigateAndCommit(inpage); 940 WaitAndCheckPreClassificationChecks(); 941 942 ExpectStartPhishingDetection(NULL); 943 ExpectShouldClassifyForMalwareResult(true); 944 } 945 946 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckXHTML) { 947 // Check that XHTML is supported, in addition to the default HTML type. 948 GURL url("http://host.com/xhtml"); 949 rvh_tester()->SetContentsMimeType("application/xhtml+xml"); 950 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 951 &kFalse, &kFalse, &kFalse, &kFalse); 952 NavigateAndCommit(url); 953 WaitAndCheckPreClassificationChecks(); 954 955 ExpectStartPhishingDetection(&url); 956 ExpectShouldClassifyForMalwareResult(true); 957 } 958 959 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckTwoNavigations) { 960 // Navigate to two hosts, which should cause two IPCs. 961 GURL url1("http://host1.com/"); 962 ExpectPreClassificationChecks(url1, &kFalse, &kFalse, &kFalse, &kFalse, 963 &kFalse, &kFalse, &kFalse, &kFalse); 964 NavigateAndCommit(url1); 965 WaitAndCheckPreClassificationChecks(); 966 967 ExpectStartPhishingDetection(&url1); 968 ExpectShouldClassifyForMalwareResult(true); 969 970 GURL url2("http://host2.com/"); 971 ExpectPreClassificationChecks(url2, &kFalse, &kFalse, &kFalse, &kFalse, 972 &kFalse, &kFalse, &kFalse, &kFalse); 973 NavigateAndCommit(url2); 974 WaitAndCheckPreClassificationChecks(); 975 976 ExpectStartPhishingDetection(&url2); 977 ExpectShouldClassifyForMalwareResult(true); 978 } 979 980 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckMimeType) { 981 // If the mime type is not one that we support, no IPC should be triggered 982 // but all pre-classification checks should run because we might classify 983 // other mime types for malware. 984 // Note: for this test to work correctly, the new URL must be on the 985 // same domain as the previous URL, otherwise it will create a new 986 // RenderViewHost that won't have the mime type set. 987 GURL url("http://host2.com/image.jpg"); 988 rvh_tester()->SetContentsMimeType("image/jpeg"); 989 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 990 &kFalse, &kFalse, &kFalse, &kFalse); 991 NavigateAndCommit(url); 992 WaitAndCheckPreClassificationChecks(); 993 994 ExpectStartPhishingDetection(NULL); 995 ExpectShouldClassifyForMalwareResult(true); 996 } 997 998 TEST_F(ClientSideDetectionHostTest, 999 TestPreClassificationCheckPrivateIpAddress) { 1000 // If IsPrivateIPAddress returns true, no IPC should be triggered. 1001 GURL url("http://host3.com/"); 1002 ExpectPreClassificationChecks(url, &kTrue, &kFalse, NULL, NULL, NULL, NULL, 1003 NULL, NULL); 1004 NavigateAndCommit(url); 1005 WaitAndCheckPreClassificationChecks(); 1006 const IPC::Message* msg = process()->sink().GetFirstMessageMatching( 1007 SafeBrowsingMsg_StartPhishingDetection::ID); 1008 ASSERT_FALSE(msg); 1009 ExpectShouldClassifyForMalwareResult(false); 1010 } 1011 1012 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckIncognito) { 1013 // If the tab is incognito there should be no IPC. Also, we shouldn't 1014 // even check the csd-whitelist. 1015 GURL url("http://host4.com/"); 1016 ExpectPreClassificationChecks(url, &kFalse, &kTrue, NULL, NULL, NULL, NULL, 1017 NULL, NULL); 1018 NavigateAndCommit(url); 1019 WaitAndCheckPreClassificationChecks(); 1020 1021 ExpectStartPhishingDetection(NULL); 1022 ExpectShouldClassifyForMalwareResult(false); 1023 } 1024 1025 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckCsdWhitelist) { 1026 // If the URL is on the csd whitelist no phishing IPC should be sent 1027 // but we should classify the URL for malware. 1028 GURL url("http://host5.com/"); 1029 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kFalse, &kFalse, 1030 &kFalse, &kFalse, &kFalse); 1031 NavigateAndCommit(url); 1032 WaitAndCheckPreClassificationChecks(); 1033 1034 ExpectStartPhishingDetection(NULL); 1035 ExpectShouldClassifyForMalwareResult(true); 1036 } 1037 1038 TEST_F(ClientSideDetectionHostTest, 1039 TestPreClassificationCheckMalwareKillSwitch) { 1040 // If the malware killswitch is on we shouldn't classify the page for malware. 1041 GURL url("http://host5.com/kill-switch"); 1042 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, &kFalse, 1043 &kFalse, &kFalse, &kFalse); 1044 NavigateAndCommit(url); 1045 WaitAndCheckPreClassificationChecks(); 1046 1047 ExpectStartPhishingDetection(&url); 1048 ExpectShouldClassifyForMalwareResult(false); 1049 } 1050 1051 TEST_F(ClientSideDetectionHostTest, 1052 TestPreClassificationCheckKillswitchAndCsdWhitelist) { 1053 // If both the malware kill-swtich is on and the URL is on the csd whitelist, 1054 // we will leave pre-classification checks early. 1055 GURL url("http://host5.com/kill-switch-and-whitelisted"); 1056 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kTrue, NULL, 1057 NULL, NULL, NULL); 1058 NavigateAndCommit(url); 1059 WaitAndCheckPreClassificationChecks(); 1060 1061 ExpectStartPhishingDetection(NULL); 1062 ExpectShouldClassifyForMalwareResult(false); 1063 } 1064 1065 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckInvalidCache) { 1066 // If item is in the cache but it isn't valid, we will classify regardless 1067 // of whether we are over the reporting limit. 1068 GURL url("http://host6.com/"); 1069 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 1070 &kFalse, &kTrue, NULL, &kFalse); 1071 1072 NavigateAndCommit(url); 1073 WaitAndCheckPreClassificationChecks(); 1074 1075 ExpectStartPhishingDetection(&url); 1076 ExpectShouldClassifyForMalwareResult(true); 1077 } 1078 1079 TEST_F(ClientSideDetectionHostTest, 1080 TestPreClassificationCheckOverPhishingReportingLimit) { 1081 // If the url isn't in the cache and we are over the reporting limit, we 1082 // don't do classification. 1083 GURL url("http://host7.com/"); 1084 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 1085 &kFalse, &kFalse, &kTrue, &kFalse); 1086 NavigateAndCommit(url); 1087 WaitAndCheckPreClassificationChecks(); 1088 1089 ExpectStartPhishingDetection(NULL); 1090 ExpectShouldClassifyForMalwareResult(true); 1091 } 1092 1093 TEST_F(ClientSideDetectionHostTest, 1094 TestPreClassificationCheckOverMalwareReportingLimit) { 1095 GURL url("http://host.com/"); 1096 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 1097 &kFalse, &kFalse, &kFalse, &kTrue); 1098 NavigateAndCommit(url); 1099 WaitAndCheckPreClassificationChecks(); 1100 1101 ExpectStartPhishingDetection(&url); 1102 ExpectShouldClassifyForMalwareResult(false); 1103 } 1104 1105 TEST_F(ClientSideDetectionHostTest, 1106 TestPreClassificationCheckOverBothReportingLimits) { 1107 GURL url("http://host.com/"); 1108 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 1109 &kFalse, &kFalse, &kTrue, &kTrue); 1110 NavigateAndCommit(url); 1111 WaitAndCheckPreClassificationChecks(); 1112 1113 ExpectStartPhishingDetection(NULL); 1114 ExpectShouldClassifyForMalwareResult(false); 1115 } 1116 1117 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckHttpsUrl) { 1118 GURL url("https://host.com/"); 1119 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, 1120 &kFalse, &kFalse, &kFalse, &kFalse); 1121 NavigateAndCommit(url); 1122 WaitAndCheckPreClassificationChecks(); 1123 1124 ExpectStartPhishingDetection(NULL); 1125 ExpectShouldClassifyForMalwareResult(true); 1126 } 1127 1128 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckValidCached) { 1129 // If result is cached, we will try and display the blocking page directly 1130 // with no start classification message. 1131 GURL url("http://host8.com/"); 1132 ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kTrue, 1133 &kFalse, &kFalse, &kFalse); 1134 1135 UnsafeResource resource; 1136 EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)) 1137 .WillOnce(SaveArg<0>(&resource)); 1138 1139 NavigateAndCommit(url); 1140 WaitAndCheckPreClassificationChecks(); 1141 EXPECT_EQ(url, resource.url); 1142 EXPECT_EQ(url, resource.original_url); 1143 1144 ExpectStartPhishingDetection(NULL); 1145 1146 // Showing a phishing warning will invalidate all the weak pointers which 1147 // means we will not extract malware features. 1148 ExpectShouldClassifyForMalwareResult(false); 1149 } 1150 } // namespace safe_browsing 1151