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 // This test creates a fake safebrowsing service, where we can inject 6 // malware and phishing urls. It then uses a real browser to go to 7 // these urls, and sends "goback" or "proceed" commands and verifies 8 // they work. 9 10 #include "base/bind.h" 11 #include "base/command_line.h" 12 #include "base/prefs/pref_service.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/values.h" 15 #include "chrome/browser/browser_process.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/safe_browsing/database_manager.h" 18 #include "chrome/browser/safe_browsing/malware_details.h" 19 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h" 20 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 21 #include "chrome/browser/safe_browsing/safe_browsing_util.h" 22 #include "chrome/browser/safe_browsing/ui_manager.h" 23 #include "chrome/browser/ui/browser.h" 24 #include "chrome/browser/ui/browser_tabstrip.h" 25 #include "chrome/browser/ui/tabs/tab_strip_model.h" 26 #include "chrome/common/pref_names.h" 27 #include "chrome/common/url_constants.h" 28 #include "chrome/test/base/in_process_browser_test.h" 29 #include "chrome/test/base/test_switches.h" 30 #include "chrome/test/base/ui_test_utils.h" 31 #include "content/public/browser/interstitial_page.h" 32 #include "content/public/browser/navigation_controller.h" 33 #include "content/public/browser/notification_types.h" 34 #include "content/public/browser/render_frame_host.h" 35 #include "content/public/browser/render_view_host.h" 36 #include "content/public/browser/web_contents.h" 37 #include "content/public/test/test_browser_thread.h" 38 #include "content/public/test/test_utils.h" 39 40 using content::BrowserThread; 41 using content::InterstitialPage; 42 using content::NavigationController; 43 using content::WebContents; 44 45 namespace { 46 47 const char kEmptyPage[] = "files/empty.html"; 48 const char kMalwarePage[] = "files/safe_browsing/malware.html"; 49 const char kMalwareIframe[] = "files/safe_browsing/malware_iframe.html"; 50 51 class InterstitialObserver : public content::WebContentsObserver { 52 public: 53 InterstitialObserver(content::WebContents* web_contents, 54 const base::Closure& attach_callback, 55 const base::Closure& detach_callback) 56 : WebContentsObserver(web_contents), 57 attach_callback_(attach_callback), 58 detach_callback_(detach_callback) { 59 } 60 61 virtual void DidAttachInterstitialPage() OVERRIDE { 62 LOG(INFO) << __FUNCTION__; 63 attach_callback_.Run(); 64 } 65 66 virtual void DidDetachInterstitialPage() OVERRIDE { 67 LOG(INFO) << __FUNCTION__; 68 detach_callback_.Run(); 69 } 70 71 private: 72 base::Closure attach_callback_; 73 base::Closure detach_callback_; 74 75 DISALLOW_COPY_AND_ASSIGN(InterstitialObserver); 76 }; 77 78 // A SafeBrowsingDatabaseManager class that allows us to inject the malicious 79 // URLs. 80 class FakeSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager { 81 public: 82 explicit FakeSafeBrowsingDatabaseManager(SafeBrowsingService* service) 83 : SafeBrowsingDatabaseManager(service) { } 84 85 // Called on the IO thread to check if the given url is safe or not. If we 86 // can synchronously determine that the url is safe, CheckUrl returns true. 87 // Otherwise it returns false, and "client" is called asynchronously with the 88 // result when it is ready. 89 // Overrides SafeBrowsingDatabaseManager::CheckBrowseUrl. 90 virtual bool CheckBrowseUrl(const GURL& gurl, Client* client) OVERRIDE { 91 if (badurls[gurl.spec()] == SB_THREAT_TYPE_SAFE) 92 return true; 93 94 BrowserThread::PostTask( 95 BrowserThread::IO, FROM_HERE, 96 base::Bind(&FakeSafeBrowsingDatabaseManager::OnCheckBrowseURLDone, 97 this, gurl, client)); 98 return false; 99 } 100 101 void OnCheckBrowseURLDone(const GURL& gurl, Client* client) { 102 std::vector<SBThreatType> expected_threats; 103 expected_threats.push_back(SB_THREAT_TYPE_URL_MALWARE); 104 expected_threats.push_back(SB_THREAT_TYPE_URL_PHISHING); 105 SafeBrowsingDatabaseManager::SafeBrowsingCheck sb_check( 106 std::vector<GURL>(1, gurl), 107 std::vector<SBFullHash>(), 108 client, 109 safe_browsing_util::MALWARE, 110 expected_threats); 111 sb_check.url_results[0] = badurls[gurl.spec()]; 112 client->OnSafeBrowsingResult(sb_check); 113 } 114 115 void SetURLThreatType(const GURL& url, SBThreatType threat_type) { 116 badurls[url.spec()] = threat_type; 117 } 118 119 private: 120 virtual ~FakeSafeBrowsingDatabaseManager() {} 121 122 base::hash_map<std::string, SBThreatType> badurls; 123 DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingDatabaseManager); 124 }; 125 126 // A SafeBrowingUIManager class that allows intercepting malware details. 127 class FakeSafeBrowsingUIManager : public SafeBrowsingUIManager { 128 public: 129 explicit FakeSafeBrowsingUIManager(SafeBrowsingService* service) : 130 SafeBrowsingUIManager(service) { } 131 132 // Overrides SafeBrowsingUIManager 133 virtual void SendSerializedMalwareDetails( 134 const std::string& serialized) OVERRIDE { 135 // Notify the UI thread that we got a report. 136 BrowserThread::PostTask( 137 BrowserThread::UI, 138 FROM_HERE, 139 base::Bind(&FakeSafeBrowsingUIManager::OnMalwareDetailsDone, 140 this, 141 serialized)); 142 } 143 144 void OnMalwareDetailsDone(const std::string& serialized) { 145 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 146 report_ = serialized; 147 148 EXPECT_FALSE(malware_details_done_callback_.is_null()); 149 if (!malware_details_done_callback_.is_null()) { 150 malware_details_done_callback_.Run(); 151 malware_details_done_callback_ = base::Closure(); 152 } 153 } 154 155 void set_malware_details_done_callback(const base::Closure& callback) { 156 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 157 EXPECT_TRUE(malware_details_done_callback_.is_null()); 158 malware_details_done_callback_ = callback; 159 } 160 161 std::string GetReport() { 162 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 163 return report_; 164 } 165 166 protected: 167 virtual ~FakeSafeBrowsingUIManager() { } 168 169 private: 170 std::string report_; 171 base::Closure malware_details_done_callback_; 172 173 DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingUIManager); 174 }; 175 176 class FakeSafeBrowsingService : public SafeBrowsingService { 177 public: 178 FakeSafeBrowsingService() 179 : fake_database_manager_(), 180 fake_ui_manager_() { } 181 182 // Returned pointer has the same lifespan as the database_manager_ refcounted 183 // object. 184 FakeSafeBrowsingDatabaseManager* fake_database_manager() { 185 return fake_database_manager_; 186 } 187 // Returned pointer has the same lifespan as the ui_manager_ refcounted 188 // object. 189 FakeSafeBrowsingUIManager* fake_ui_manager() { 190 return fake_ui_manager_; 191 } 192 193 protected: 194 virtual ~FakeSafeBrowsingService() { } 195 196 virtual SafeBrowsingDatabaseManager* CreateDatabaseManager() OVERRIDE { 197 fake_database_manager_ = new FakeSafeBrowsingDatabaseManager(this); 198 return fake_database_manager_; 199 } 200 201 virtual SafeBrowsingUIManager* CreateUIManager() OVERRIDE { 202 fake_ui_manager_ = new FakeSafeBrowsingUIManager(this); 203 return fake_ui_manager_; 204 } 205 206 private: 207 FakeSafeBrowsingDatabaseManager* fake_database_manager_; 208 FakeSafeBrowsingUIManager* fake_ui_manager_; 209 210 DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService); 211 }; 212 213 // Factory that creates FakeSafeBrowsingService instances. 214 class TestSafeBrowsingServiceFactory : public SafeBrowsingServiceFactory { 215 public: 216 TestSafeBrowsingServiceFactory() : 217 most_recent_service_(NULL) { } 218 virtual ~TestSafeBrowsingServiceFactory() { } 219 220 virtual SafeBrowsingService* CreateSafeBrowsingService() OVERRIDE { 221 most_recent_service_ = new FakeSafeBrowsingService(); 222 return most_recent_service_; 223 } 224 225 FakeSafeBrowsingService* most_recent_service() const { 226 return most_recent_service_; 227 } 228 229 private: 230 FakeSafeBrowsingService* most_recent_service_; 231 }; 232 233 // A MalwareDetails class lets us intercept calls from the renderer. 234 class FakeMalwareDetails : public MalwareDetails { 235 public: 236 FakeMalwareDetails( 237 SafeBrowsingUIManager* delegate, 238 WebContents* web_contents, 239 const SafeBrowsingUIManager::UnsafeResource& unsafe_resource) 240 : MalwareDetails(delegate, web_contents, unsafe_resource), 241 got_dom_(false), 242 waiting_(false) { } 243 244 virtual void AddDOMDetails( 245 const std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node>& params) 246 OVERRIDE { 247 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 248 MalwareDetails::AddDOMDetails(params); 249 250 // Notify the UI thread that we got the dom details. 251 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 252 base::Bind(&FakeMalwareDetails::OnDOMDetailsDone, 253 this)); 254 } 255 256 void WaitForDOM() { 257 if (got_dom_) { 258 LOG(INFO) << "Already got the dom details."; 259 return; 260 } 261 // This condition might not trigger normally, but if you add a 262 // sleep(1) in malware_dom_details it triggers :). 263 waiting_ = true; 264 LOG(INFO) << "Waiting for dom details."; 265 content::RunMessageLoop(); 266 EXPECT_TRUE(got_dom_); 267 } 268 269 private: 270 virtual ~FakeMalwareDetails() {} 271 272 void OnDOMDetailsDone() { 273 got_dom_ = true; 274 if (waiting_) { 275 base::MessageLoopForUI::current()->Quit(); 276 } 277 } 278 279 // Some logic to figure out if we should wait for the dom details or not. 280 // These variables should only be accessed in the UI thread. 281 bool got_dom_; 282 bool waiting_; 283 }; 284 285 class TestMalwareDetailsFactory : public MalwareDetailsFactory { 286 public: 287 TestMalwareDetailsFactory() : details_() { } 288 virtual ~TestMalwareDetailsFactory() { } 289 290 virtual MalwareDetails* CreateMalwareDetails( 291 SafeBrowsingUIManager* delegate, 292 WebContents* web_contents, 293 const SafeBrowsingUIManager::UnsafeResource& unsafe_resource) OVERRIDE { 294 details_ = new FakeMalwareDetails(delegate, web_contents, 295 unsafe_resource); 296 return details_; 297 } 298 299 FakeMalwareDetails* get_details() { 300 return details_; 301 } 302 303 private: 304 FakeMalwareDetails* details_; 305 }; 306 307 // A SafeBrowingBlockingPage class that lets us wait until it's hidden. 308 class TestSafeBrowsingBlockingPageV2 : public SafeBrowsingBlockingPageV2 { 309 public: 310 TestSafeBrowsingBlockingPageV2(SafeBrowsingUIManager* manager, 311 WebContents* web_contents, 312 const UnsafeResourceList& unsafe_resources) 313 : SafeBrowsingBlockingPageV2(manager, web_contents, unsafe_resources), 314 wait_for_delete_(false) { 315 // Don't wait the whole 3 seconds for the browser test. 316 malware_details_proceed_delay_ms_ = 100; 317 } 318 319 virtual ~TestSafeBrowsingBlockingPageV2() { 320 LOG(INFO) << __FUNCTION__; 321 if (!wait_for_delete_) 322 return; 323 324 // Notify that we are gone 325 base::MessageLoopForUI::current()->Quit(); 326 wait_for_delete_ = false; 327 } 328 329 void WaitForDelete() { 330 LOG(INFO) << __FUNCTION__; 331 wait_for_delete_ = true; 332 content::RunMessageLoop(); 333 } 334 335 // InterstitialPageDelegate methods: 336 virtual void CommandReceived(const std::string& command) OVERRIDE { 337 LOG(INFO) << __FUNCTION__ << " " << command; 338 SafeBrowsingBlockingPageV2::CommandReceived(command); 339 } 340 virtual void OnProceed() OVERRIDE { 341 LOG(INFO) << __FUNCTION__; 342 SafeBrowsingBlockingPageV2::OnProceed(); 343 } 344 virtual void OnDontProceed() OVERRIDE { 345 LOG(INFO) << __FUNCTION__; 346 SafeBrowsingBlockingPageV2::OnDontProceed(); 347 } 348 349 private: 350 bool wait_for_delete_; 351 }; 352 353 // A SafeBrowingBlockingPage class that lets us wait until it's hidden. 354 class TestSafeBrowsingBlockingPageV3 : public SafeBrowsingBlockingPageV3 { 355 public: 356 TestSafeBrowsingBlockingPageV3(SafeBrowsingUIManager* manager, 357 WebContents* web_contents, 358 const UnsafeResourceList& unsafe_resources) 359 : SafeBrowsingBlockingPageV3(manager, web_contents, unsafe_resources), 360 wait_for_delete_(false) { 361 // Don't wait the whole 3 seconds for the browser test. 362 malware_details_proceed_delay_ms_ = 100; 363 } 364 365 virtual ~TestSafeBrowsingBlockingPageV3() { 366 LOG(INFO) << __FUNCTION__; 367 if (!wait_for_delete_) 368 return; 369 370 // Notify that we are gone 371 base::MessageLoopForUI::current()->Quit(); 372 wait_for_delete_ = false; 373 } 374 375 void WaitForDelete() { 376 LOG(INFO) << __FUNCTION__; 377 wait_for_delete_ = true; 378 content::RunMessageLoop(); 379 } 380 381 // InterstitialPageDelegate methods: 382 virtual void CommandReceived(const std::string& command) OVERRIDE { 383 LOG(INFO) << __FUNCTION__ << " " << command; 384 SafeBrowsingBlockingPageV3::CommandReceived(command); 385 } 386 virtual void OnProceed() OVERRIDE { 387 LOG(INFO) << __FUNCTION__; 388 SafeBrowsingBlockingPageV3::OnProceed(); 389 } 390 virtual void OnDontProceed() OVERRIDE { 391 LOG(INFO) << __FUNCTION__; 392 SafeBrowsingBlockingPageV3::OnDontProceed(); 393 } 394 395 private: 396 bool wait_for_delete_; 397 }; 398 399 class TestSafeBrowsingBlockingPageFactory 400 : public SafeBrowsingBlockingPageFactory { 401 public: 402 TestSafeBrowsingBlockingPageFactory() : version_(2) { } 403 virtual ~TestSafeBrowsingBlockingPageFactory() { } 404 405 virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage( 406 SafeBrowsingUIManager* delegate, 407 WebContents* web_contents, 408 const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) 409 OVERRIDE { 410 if (version_ == 3) { 411 return new TestSafeBrowsingBlockingPageV3(delegate, web_contents, 412 unsafe_resources); 413 } 414 return new TestSafeBrowsingBlockingPageV2(delegate, web_contents, 415 unsafe_resources); 416 } 417 418 void SetTestVersion(int version) { 419 version_ = version; 420 } 421 422 private: 423 int version_; 424 }; 425 426 } // namespace 427 428 // Tests the safe browsing blocking page in a browser. 429 class SafeBrowsingBlockingPageBrowserTest 430 : public InProcessBrowserTest, 431 public testing::WithParamInterface<int> { 432 public: 433 enum Visibility { 434 VISIBILITY_ERROR = -1, 435 HIDDEN = 0, 436 VISIBLE = 1 437 }; 438 439 SafeBrowsingBlockingPageBrowserTest() { 440 } 441 442 virtual void SetUp() OVERRIDE { 443 SafeBrowsingService::RegisterFactory(&factory_); 444 SafeBrowsingBlockingPage::RegisterFactory(&blocking_page_factory_); 445 blocking_page_factory_.SetTestVersion(GetParam()); 446 MalwareDetails::RegisterFactory(&details_factory_); 447 InProcessBrowserTest::SetUp(); 448 } 449 450 virtual void TearDown() OVERRIDE { 451 InProcessBrowserTest::TearDown(); 452 SafeBrowsingBlockingPage::RegisterFactory(NULL); 453 SafeBrowsingService::RegisterFactory(NULL); 454 MalwareDetails::RegisterFactory(NULL); 455 } 456 457 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { 458 ASSERT_TRUE(test_server()->Start()); 459 } 460 461 void SetURLThreatType(const GURL& url, SBThreatType threat_type) { 462 FakeSafeBrowsingService* service = 463 static_cast<FakeSafeBrowsingService*>( 464 g_browser_process->safe_browsing_service()); 465 466 ASSERT_TRUE(service); 467 service->fake_database_manager()->SetURLThreatType(url, threat_type); 468 } 469 470 // Adds a safebrowsing result of type |threat_type| to the fake safebrowsing 471 // service, navigates to that page, and returns the url. 472 GURL SetupWarningAndNavigate(SBThreatType threat_type) { 473 GURL url = test_server()->GetURL(kEmptyPage); 474 SetURLThreatType(url, threat_type); 475 476 ui_test_utils::NavigateToURL(browser(), url); 477 EXPECT_TRUE(WaitForReady()); 478 return url; 479 } 480 481 // Adds a safebrowsing malware result to the fake safebrowsing service, 482 // navigates to a page with an iframe containing the malware site, and 483 // returns the url of the parent page. 484 GURL SetupMalwareIframeWarningAndNavigate() { 485 GURL url = test_server()->GetURL(kMalwarePage); 486 GURL iframe_url = test_server()->GetURL(kMalwareIframe); 487 SetURLThreatType(iframe_url, SB_THREAT_TYPE_URL_MALWARE); 488 489 LOG(INFO) << "navigating... " << url.spec(); 490 ui_test_utils::NavigateToURL(browser(), url); 491 EXPECT_TRUE(WaitForReady()); 492 return url; 493 } 494 495 void SendCommand(const std::string& command) { 496 WebContents* contents = 497 browser()->tab_strip_model()->GetActiveWebContents(); 498 // We use InterstitialPage::GetInterstitialPage(tab) instead of 499 // tab->GetInterstitialPage() because the tab doesn't have a pointer 500 // to its interstital page until it gets a command from the renderer 501 // that it has indeed displayed it -- and this sometimes happens after 502 // NavigateToURL returns. 503 SafeBrowsingBlockingPage* interstitial_page = 504 static_cast<SafeBrowsingBlockingPage*>( 505 InterstitialPage::GetInterstitialPage(contents)-> 506 GetDelegateForTesting()); 507 ASSERT_TRUE(interstitial_page); 508 interstitial_page->CommandReceived(command); 509 } 510 511 void DontProceedThroughInterstitial() { 512 WebContents* contents = 513 browser()->tab_strip_model()->GetActiveWebContents(); 514 InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage( 515 contents); 516 ASSERT_TRUE(interstitial_page); 517 interstitial_page->DontProceed(); 518 } 519 520 void ProceedThroughInterstitial() { 521 WebContents* contents = 522 browser()->tab_strip_model()->GetActiveWebContents(); 523 InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage( 524 contents); 525 ASSERT_TRUE(interstitial_page); 526 interstitial_page->Proceed(); 527 } 528 529 void AssertNoInterstitial(bool wait_for_delete) { 530 WebContents* contents = 531 browser()->tab_strip_model()->GetActiveWebContents(); 532 533 if (contents->ShowingInterstitialPage() && wait_for_delete) { 534 // We'll get notified when the interstitial is deleted. 535 if (GetParam() == 3) { 536 TestSafeBrowsingBlockingPageV3* page = 537 static_cast<TestSafeBrowsingBlockingPageV3*>( 538 contents->GetInterstitialPage()->GetDelegateForTesting()); 539 page->WaitForDelete(); 540 } else { 541 TestSafeBrowsingBlockingPageV2* page = 542 static_cast<TestSafeBrowsingBlockingPageV2*>( 543 contents->GetInterstitialPage()->GetDelegateForTesting()); 544 page->WaitForDelete(); 545 } 546 } 547 548 // Can't use InterstitialPage::GetInterstitialPage() because that 549 // gets updated after the TestSafeBrowsingBlockingPage destructor 550 ASSERT_FALSE(contents->ShowingInterstitialPage()); 551 } 552 553 bool YesInterstitial() { 554 WebContents* contents = 555 browser()->tab_strip_model()->GetActiveWebContents(); 556 InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage( 557 contents); 558 return interstitial_page != NULL; 559 } 560 561 void WaitForInterstitial() { 562 WebContents* contents = 563 browser()->tab_strip_model()->GetActiveWebContents(); 564 scoped_refptr<content::MessageLoopRunner> loop_runner( 565 new content::MessageLoopRunner); 566 InterstitialObserver observer(contents, 567 loop_runner->QuitClosure(), 568 base::Closure()); 569 if (!InterstitialPage::GetInterstitialPage(contents)) 570 loop_runner->Run(); 571 } 572 573 void SetReportSentCallback(const base::Closure& callback) { 574 LOG(INFO) << __FUNCTION__; 575 factory_.most_recent_service() 576 ->fake_ui_manager() 577 ->set_malware_details_done_callback(callback); 578 } 579 580 std::string GetReportSent() { 581 LOG(INFO) << __FUNCTION__; 582 return factory_.most_recent_service()->fake_ui_manager()->GetReport(); 583 } 584 585 void MalwareRedirectCancelAndProceed(const std::string& open_function) { 586 GURL load_url = test_server()->GetURL( 587 "files/safe_browsing/interstitial_cancel.html"); 588 GURL malware_url("http://localhost/files/safe_browsing/malware.html"); 589 SetURLThreatType(malware_url, SB_THREAT_TYPE_URL_MALWARE); 590 591 // Load the test page. 592 ui_test_utils::NavigateToURL(browser(), load_url); 593 // Trigger the safe browsing interstitial page via a redirect in 594 // "openWin()". 595 ui_test_utils::NavigateToURLWithDisposition( 596 browser(), 597 GURL("javascript:" + open_function + "()"), 598 CURRENT_TAB, 599 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 600 WaitForInterstitial(); 601 // Cancel the redirect request while interstitial page is open. 602 browser()->tab_strip_model()->ActivateTabAt(0, true); 603 ui_test_utils::NavigateToURLWithDisposition( 604 browser(), 605 GURL("javascript:stopWin()"), 606 CURRENT_TAB, 607 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 608 browser()->tab_strip_model()->ActivateTabAt(1, true); 609 // Simulate the user clicking "proceed", there should be no crash. Since 610 // clicking proceed may do nothing (see comment in MalwareRedirectCanceled 611 // below, and crbug.com/76460), we use SendCommand to trigger the callback 612 // directly rather than using ClickAndWaitForDetach since there might not 613 // be a notification to wait for. 614 SendCommand("\"proceed\""); 615 } 616 617 content::RenderViewHost* GetRenderViewHost() { 618 InterstitialPage* interstitial = InterstitialPage::GetInterstitialPage( 619 browser()->tab_strip_model()->GetActiveWebContents()); 620 if (!interstitial) 621 return NULL; 622 return interstitial->GetRenderViewHostForTesting(); 623 } 624 625 bool WaitForReady() { 626 LOG(INFO) << __FUNCTION__; 627 content::RenderViewHost* rvh = GetRenderViewHost(); 628 if (!rvh) 629 return false; 630 // Wait until all <script> tags have executed, including jstemplate. 631 // TODO(joaodasilva): it would be nice to avoid the busy loop, though in 632 // practice it spins at most once or twice. 633 std::string ready_state; 634 do { 635 scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue( 636 rvh->GetMainFrame(), "document.readyState"); 637 if (!value.get() || !value->GetAsString(&ready_state)) 638 return false; 639 } while (ready_state != "complete"); 640 LOG(INFO) << "done waiting"; 641 return true; 642 } 643 644 Visibility GetVisibility(const std::string& node_id) { 645 content::RenderViewHost* rvh = GetRenderViewHost(); 646 if (!rvh) 647 return VISIBILITY_ERROR; 648 scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue( 649 rvh->GetMainFrame(), 650 "var node = document.getElementById('" + node_id + "');\n" 651 "if (node)\n" 652 " node.offsetWidth > 0 && node.offsetHeight > 0;" 653 "else\n" 654 " 'node not found';\n"); 655 if (!value.get()) 656 return VISIBILITY_ERROR; 657 bool result = false; 658 if (!value->GetAsBoolean(&result)) 659 return VISIBILITY_ERROR; 660 return result ? VISIBLE : HIDDEN; 661 } 662 663 bool Click(const std::string& node_id) { 664 LOG(INFO) << "Click " << node_id; 665 content::RenderViewHost* rvh = GetRenderViewHost(); 666 if (!rvh) 667 return false; 668 // We don't use ExecuteScriptAndGetValue for this one, since clicking 669 // the button/link may navigate away before the injected javascript can 670 // reply, hanging the test. 671 rvh->GetMainFrame()->ExecuteJavaScript( 672 base::ASCIIToUTF16( 673 "document.getElementById('" + node_id + "').click();\n")); 674 return true; 675 } 676 677 bool ClickAndWaitForDetach(const std::string& node_id) { 678 // We wait for interstitial_detached rather than nav_entry_committed, as 679 // going back from a main-frame malware interstitial page will not cause a 680 // nav entry committed event. 681 scoped_refptr<content::MessageLoopRunner> loop_runner( 682 new content::MessageLoopRunner); 683 InterstitialObserver observer( 684 browser()->tab_strip_model()->GetActiveWebContents(), 685 base::Closure(), 686 loop_runner->QuitClosure()); 687 if (!Click(node_id)) 688 return false; 689 loop_runner->Run(); 690 return true; 691 } 692 693 protected: 694 TestMalwareDetailsFactory details_factory_; 695 696 private: 697 TestSafeBrowsingServiceFactory factory_; 698 TestSafeBrowsingBlockingPageFactory blocking_page_factory_; 699 700 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageBrowserTest); 701 }; 702 703 INSTANTIATE_TEST_CASE_P(SafeBrowsingInterstitialVersions, 704 SafeBrowsingBlockingPageBrowserTest, 705 testing::Values(2, 3)); 706 707 // TODO(linux_aura) http://crbug.com/163931 708 // TODO(win_aura) http://crbug.com/154081 709 #if defined(USE_AURA) && !defined(OS_CHROMEOS) 710 #define MAYBE_MalwareRedirectInIFrameCanceled DISABLED_MalwareRedirectInIFrameCanceled 711 #else 712 #define MAYBE_MalwareRedirectInIFrameCanceled MalwareRedirectInIFrameCanceled 713 #endif 714 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 715 MAYBE_MalwareRedirectInIFrameCanceled) { 716 // 1. Test the case that redirect is a subresource. 717 MalwareRedirectCancelAndProceed("openWinIFrame"); 718 // If the redirect was from subresource but canceled, "proceed" will continue 719 // with the rest of resources. 720 AssertNoInterstitial(true); 721 } 722 723 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 724 MalwareRedirectCanceled) { 725 // 2. Test the case that redirect is the only resource. 726 MalwareRedirectCancelAndProceed("openWin"); 727 // Clicking proceed won't do anything if the main request is cancelled 728 // already. See crbug.com/76460. 729 EXPECT_TRUE(YesInterstitial()); 730 } 731 732 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 733 MalwareDontProceed) { 734 #if defined(OS_WIN) && defined(USE_ASH) 735 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 736 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 737 return; 738 #endif 739 740 SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE); 741 742 if (GetParam() == 2) { 743 EXPECT_EQ(VISIBLE, GetVisibility("malware-icon")); 744 EXPECT_EQ(HIDDEN, GetVisibility("subresource-icon")); 745 EXPECT_EQ(HIDDEN, GetVisibility("phishing-icon")); 746 EXPECT_EQ(VISIBLE, GetVisibility("check-report")); 747 EXPECT_EQ(HIDDEN, GetVisibility("show-diagnostic-link")); 748 EXPECT_EQ(HIDDEN, GetVisibility("report-error-link")); 749 EXPECT_EQ(HIDDEN, GetVisibility("proceed")); 750 EXPECT_TRUE(Click("see-more-link")); 751 EXPECT_EQ(VISIBLE, GetVisibility("show-diagnostic-link")); 752 EXPECT_EQ(HIDDEN, GetVisibility("report-error-link")); 753 EXPECT_EQ(VISIBLE, GetVisibility("proceed")); 754 EXPECT_TRUE(ClickAndWaitForDetach("back")); 755 } else { 756 EXPECT_EQ(VISIBLE, GetVisibility("primary-button")); 757 EXPECT_EQ(HIDDEN, GetVisibility("details")); 758 EXPECT_EQ(HIDDEN, GetVisibility("proceed-link")); 759 EXPECT_EQ(HIDDEN, GetVisibility("error-code")); 760 EXPECT_TRUE(Click("details-button")); 761 EXPECT_EQ(VISIBLE, GetVisibility("details")); 762 EXPECT_EQ(VISIBLE, GetVisibility("proceed-link")); 763 EXPECT_EQ(HIDDEN, GetVisibility("error-code")); 764 EXPECT_TRUE(ClickAndWaitForDetach("primary-button")); 765 } 766 767 AssertNoInterstitial(false); // Assert the interstitial is gone 768 EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank" 769 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 770 } 771 772 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, MalwareProceed) { 773 GURL url = SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE); 774 775 if (GetParam() == 2) 776 EXPECT_TRUE(ClickAndWaitForDetach("proceed")); 777 else 778 EXPECT_TRUE(ClickAndWaitForDetach("proceed-link")); 779 AssertNoInterstitial(true); // Assert the interstitial is gone. 780 EXPECT_EQ(url, 781 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 782 } 783 784 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 785 MalwareLearnMoreV2) { 786 if (GetParam() == 3) return; // Don't have this link in V3. 787 SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE); 788 789 EXPECT_TRUE(ClickAndWaitForDetach("learn-more-link")); 790 AssertNoInterstitial(false); // Assert the interstitial is gone 791 792 // We are in the help page. 793 EXPECT_EQ( 794 "/transparencyreport/safebrowsing/", 795 browser()->tab_strip_model()->GetActiveWebContents()->GetURL().path()); 796 } 797 798 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 799 MalwareIframeDontProceed) { 800 #if defined(OS_WIN) && defined(USE_ASH) 801 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 802 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 803 return; 804 #endif 805 806 SetupMalwareIframeWarningAndNavigate(); 807 808 if (GetParam() == 2) { 809 EXPECT_EQ(HIDDEN, GetVisibility("malware-icon")); 810 EXPECT_EQ(VISIBLE, GetVisibility("subresource-icon")); 811 EXPECT_EQ(HIDDEN, GetVisibility("phishing-icon")); 812 EXPECT_EQ(VISIBLE, GetVisibility("check-report")); 813 EXPECT_EQ(HIDDEN, GetVisibility("show-diagnostic-link")); 814 EXPECT_EQ(HIDDEN, GetVisibility("report-error-link")); 815 EXPECT_EQ(HIDDEN, GetVisibility("proceed")); 816 EXPECT_TRUE(Click("see-more-link")); 817 EXPECT_EQ(VISIBLE, GetVisibility("show-diagnostic-link")); 818 EXPECT_EQ(HIDDEN, GetVisibility("report-error-link")); 819 EXPECT_EQ(VISIBLE, GetVisibility("proceed")); 820 EXPECT_TRUE(ClickAndWaitForDetach("back")); 821 } else { 822 EXPECT_EQ(VISIBLE, GetVisibility("primary-button")); 823 EXPECT_EQ(HIDDEN, GetVisibility("details")); 824 EXPECT_EQ(HIDDEN, GetVisibility("proceed-link")); 825 EXPECT_EQ(HIDDEN, GetVisibility("error-code")); 826 EXPECT_TRUE(Click("details-button")); 827 EXPECT_EQ(VISIBLE, GetVisibility("details")); 828 EXPECT_EQ(VISIBLE, GetVisibility("proceed-link")); 829 EXPECT_EQ(HIDDEN, GetVisibility("error-code")); 830 EXPECT_TRUE(ClickAndWaitForDetach("primary-button")); 831 } 832 833 AssertNoInterstitial(false); // Assert the interstitial is gone 834 835 EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank" 836 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 837 } 838 839 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 840 MalwareIframeProceed) { 841 GURL url = SetupMalwareIframeWarningAndNavigate(); 842 843 if (GetParam() == 2) 844 EXPECT_TRUE(ClickAndWaitForDetach("proceed")); 845 else 846 EXPECT_TRUE(ClickAndWaitForDetach("proceed-link")); 847 AssertNoInterstitial(true); // Assert the interstitial is gone 848 849 EXPECT_EQ(url, 850 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 851 } 852 853 // http://crbug.com/273302 854 #if defined(OS_WIN) 855 // Temporarily re-enabled to get some logs. 856 #define MAYBE_MalwareIframeReportDetails MalwareIframeReportDetails 857 #else 858 #define MAYBE_MalwareIframeReportDetails MalwareIframeReportDetails 859 #endif 860 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 861 MAYBE_MalwareIframeReportDetails) { 862 // TODO(felt): Enable for V3 when the checkbox is added. 863 if (GetParam() == 3) return; 864 865 scoped_refptr<content::MessageLoopRunner> malware_report_sent_runner( 866 new content::MessageLoopRunner); 867 SetReportSentCallback(malware_report_sent_runner->QuitClosure()); 868 869 GURL url = SetupMalwareIframeWarningAndNavigate(); 870 871 LOG(INFO) << "1"; 872 873 // If the DOM details from renderer did not already return, wait for them. 874 details_factory_.get_details()->WaitForDOM(); 875 LOG(INFO) << "2"; 876 877 EXPECT_TRUE(Click("check-report")); 878 LOG(INFO) << "3"; 879 880 EXPECT_TRUE(ClickAndWaitForDetach("proceed")); 881 LOG(INFO) << "4"; 882 AssertNoInterstitial(true); // Assert the interstitial is gone 883 LOG(INFO) << "5"; 884 885 ASSERT_TRUE(browser()->profile()->GetPrefs()->GetBoolean( 886 prefs::kSafeBrowsingExtendedReportingEnabled)); 887 LOG(INFO) << "6"; 888 889 EXPECT_EQ(url, 890 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 891 LOG(INFO) << "7"; 892 893 malware_report_sent_runner->Run(); 894 std::string serialized = GetReportSent(); 895 safe_browsing::ClientMalwareReportRequest report; 896 ASSERT_TRUE(report.ParseFromString(serialized)); 897 // Verify the report is complete. 898 EXPECT_TRUE(report.complete()); 899 LOG(INFO) << "8"; 900 } 901 902 // Verifies that the "proceed anyway" link isn't available when it is disabled 903 // by the corresponding policy. Also verifies that sending the "proceed" 904 // command anyway doesn't advance to the malware site. 905 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, ProceedDisabled) { 906 #if defined(OS_WIN) && defined(USE_ASH) 907 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 908 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 909 return; 910 #endif 911 if (GetParam() == 3) return; 912 913 // Simulate a policy disabling the "proceed anyway" link. 914 browser()->profile()->GetPrefs()->SetBoolean( 915 prefs::kSafeBrowsingProceedAnywayDisabled, true); 916 917 SetupWarningAndNavigate(SB_THREAT_TYPE_URL_MALWARE); 918 919 if (GetParam() == 2) { 920 EXPECT_EQ(VISIBLE, GetVisibility("check-report")); 921 EXPECT_EQ(HIDDEN, GetVisibility("show-diagnostic-link")); 922 EXPECT_EQ(HIDDEN, GetVisibility("proceed")); 923 EXPECT_EQ(HIDDEN, GetVisibility("proceed-span")); 924 EXPECT_TRUE(Click("see-more-link")); 925 EXPECT_EQ(VISIBLE, GetVisibility("show-diagnostic-link")); 926 EXPECT_EQ(HIDDEN, GetVisibility("proceed")); 927 EXPECT_EQ(HIDDEN, GetVisibility("proceed-span")); 928 EXPECT_TRUE(ClickAndWaitForDetach("proceed")); 929 } else { 930 EXPECT_EQ(VISIBLE, GetVisibility("primary-button")); 931 EXPECT_EQ(HIDDEN, GetVisibility("details")); 932 EXPECT_EQ(HIDDEN, GetVisibility("proceed-link")); 933 EXPECT_EQ(HIDDEN, GetVisibility("final-paragraph")); 934 EXPECT_TRUE(Click("details-button")); 935 EXPECT_EQ(HIDDEN, GetVisibility("proceed-link")); 936 EXPECT_EQ(HIDDEN, GetVisibility("final-paragraph")); 937 SendCommand("proceed"); 938 } 939 940 // The "proceed" command should go back instead, if proceeding is disabled. 941 AssertNoInterstitial(true); 942 EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank" 943 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 944 } 945 946 // Verifies that the reporting checkbox is hidden on non-HTTP pages. 947 // TODO(mattm): Should also verify that no report is sent, but there isn't a 948 // good way to do that in the current design. 949 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, ReportingDisabled) { 950 #if defined(OS_WIN) && defined(USE_ASH) 951 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 952 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 953 return; 954 #endif 955 // TODO(felt): Enable for V3 when the checkbox is added. 956 if (GetParam() == 3) return; 957 958 browser()->profile()->GetPrefs()->SetBoolean( 959 prefs::kSafeBrowsingExtendedReportingEnabled, true); 960 961 net::SpawnedTestServer https_server( 962 net::SpawnedTestServer::TYPE_HTTPS, net::SpawnedTestServer::kLocalhost, 963 base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))); 964 ASSERT_TRUE(https_server.Start()); 965 GURL url = https_server.GetURL(kEmptyPage); 966 SetURLThreatType(url, SB_THREAT_TYPE_URL_MALWARE); 967 ui_test_utils::NavigateToURL(browser(), url); 968 ASSERT_TRUE(WaitForReady()); 969 970 EXPECT_EQ(HIDDEN, GetVisibility("check-report")); 971 EXPECT_EQ(HIDDEN, GetVisibility("show-diagnostic-link")); 972 EXPECT_EQ(HIDDEN, GetVisibility("proceed")); 973 EXPECT_TRUE(Click("see-more-link")); 974 EXPECT_EQ(VISIBLE, GetVisibility("show-diagnostic-link")); 975 EXPECT_EQ(VISIBLE, GetVisibility("proceed")); 976 977 EXPECT_TRUE(ClickAndWaitForDetach("back")); 978 AssertNoInterstitial(false); // Assert the interstitial is gone 979 EXPECT_EQ(GURL(url::kAboutBlankURL), // Back to "about:blank" 980 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 981 } 982 983 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 984 PhishingDontProceed) { 985 #if defined(OS_WIN) && defined(USE_ASH) 986 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 987 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 988 return; 989 #endif 990 991 SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING); 992 993 if (GetParam() == 2) { 994 EXPECT_EQ(HIDDEN, GetVisibility("malware-icon")); 995 EXPECT_EQ(HIDDEN, GetVisibility("subresource-icon")); 996 EXPECT_EQ(VISIBLE, GetVisibility("phishing-icon")); 997 EXPECT_EQ(HIDDEN, GetVisibility("check-report")); 998 EXPECT_EQ(HIDDEN, GetVisibility("show-diagnostic-link")); 999 EXPECT_EQ(HIDDEN, GetVisibility("report-error-link")); 1000 EXPECT_EQ(HIDDEN, GetVisibility("proceed")); 1001 EXPECT_TRUE(Click("see-more-link")); 1002 EXPECT_EQ(HIDDEN, GetVisibility("show-diagnostic-link")); 1003 EXPECT_EQ(VISIBLE, GetVisibility("report-error-link")); 1004 EXPECT_EQ(VISIBLE, GetVisibility("proceed")); 1005 EXPECT_TRUE(ClickAndWaitForDetach("back")); 1006 } else { 1007 EXPECT_EQ(VISIBLE, GetVisibility("primary-button")); 1008 EXPECT_EQ(HIDDEN, GetVisibility("details")); 1009 EXPECT_EQ(HIDDEN, GetVisibility("proceed-link")); 1010 EXPECT_EQ(HIDDEN, GetVisibility("error-code")); 1011 EXPECT_TRUE(Click("details-button")); 1012 EXPECT_EQ(VISIBLE, GetVisibility("details")); 1013 EXPECT_EQ(VISIBLE, GetVisibility("proceed-link")); 1014 EXPECT_EQ(HIDDEN, GetVisibility("error-code")); 1015 EXPECT_TRUE(ClickAndWaitForDetach("primary-button")); 1016 } 1017 1018 AssertNoInterstitial(false); // Assert the interstitial is gone 1019 EXPECT_EQ(GURL(url::kAboutBlankURL), // We are back to "about:blank". 1020 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 1021 } 1022 1023 // http://crbug.com/247763 1024 #if defined(OS_WIN) 1025 // Temporarily re-enabled to get some logs. 1026 #define MAYBE_PhishingProceed PhishingProceed 1027 #else 1028 #define MAYBE_PhishingProceed PhishingProceed 1029 #endif 1030 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 1031 MAYBE_PhishingProceed) { 1032 GURL url = SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING); 1033 LOG(INFO) << "1"; 1034 1035 if (GetParam() == 2) 1036 EXPECT_TRUE(ClickAndWaitForDetach("proceed")); 1037 else 1038 EXPECT_TRUE(ClickAndWaitForDetach("proceed-link")); 1039 LOG(INFO) << "2"; 1040 AssertNoInterstitial(true); // Assert the interstitial is gone 1041 LOG(INFO) << "3"; 1042 EXPECT_EQ(url, 1043 browser()->tab_strip_model()->GetActiveWebContents()->GetURL()); 1044 LOG(INFO) << "4"; 1045 } 1046 1047 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 1048 PhishingReportErrorV2) { 1049 if (GetParam() == 3) return; // Not supported in V3. 1050 SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING); 1051 1052 EXPECT_TRUE(ClickAndWaitForDetach("report-error-link")); 1053 AssertNoInterstitial(false); // Assert the interstitial is gone 1054 1055 // We are in the error reporting page. 1056 EXPECT_EQ( 1057 "/safebrowsing/report_error/", 1058 browser()->tab_strip_model()->GetActiveWebContents()->GetURL().path()); 1059 } 1060 1061 // See crbug.com/248447 1062 #if defined(OS_WIN) 1063 // Temporarily re-enabled to get some logs. 1064 #define MAYBE_PhishingLearnMore PhishingLearnMore 1065 #else 1066 #define MAYBE_PhishingLearnMore PhishingLearnMore 1067 #endif 1068 1069 IN_PROC_BROWSER_TEST_P(SafeBrowsingBlockingPageBrowserTest, 1070 MAYBE_PhishingLearnMore) { 1071 SetupWarningAndNavigate(SB_THREAT_TYPE_URL_PHISHING); 1072 LOG(INFO) << "1"; 1073 1074 if (GetParam() == 2) 1075 EXPECT_TRUE(ClickAndWaitForDetach("learn-more-link")); 1076 else 1077 EXPECT_TRUE(ClickAndWaitForDetach("help-link")); 1078 LOG(INFO) << "2"; 1079 AssertNoInterstitial(false); // Assert the interstitial is gone 1080 1081 LOG(INFO) << "3"; 1082 // We are in the help page. 1083 EXPECT_EQ( 1084 "/transparencyreport/safebrowsing/", 1085 browser()->tab_strip_model()->GetActiveWebContents()->GetURL().path()); 1086 LOG(INFO) << "4"; 1087 } 1088