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