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