1 // Copyright (c) 2011 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 "chrome/browser/browser_process.h" 11 #include "chrome/browser/prefs/pref_service.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/safe_browsing/malware_details.h" 14 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 15 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h" 16 #include "chrome/browser/ui/browser.h" 17 #include "chrome/common/pref_names.h" 18 #include "chrome/common/url_constants.h" 19 #include "chrome/test/in_process_browser_test.h" 20 #include "chrome/test/ui_test_utils.h" 21 #include "content/browser/browser_thread.h" 22 #include "content/browser/renderer_host/render_process_host.h" 23 #include "content/browser/renderer_host/resource_dispatcher_host.h" 24 #include "content/browser/tab_contents/tab_contents.h" 25 #include "content/browser/tab_contents/tab_contents_view.h" 26 27 // A SafeBrowingService class that allows us to inject the malicious URLs. 28 class FakeSafeBrowsingService : public SafeBrowsingService { 29 public: 30 FakeSafeBrowsingService() {} 31 32 virtual ~FakeSafeBrowsingService() {} 33 34 // Called on the IO thread to check if the given url is safe or not. If we 35 // can synchronously determine that the url is safe, CheckUrl returns true. 36 // Otherwise it returns false, and "client" is called asynchronously with the 37 // result when it is ready. 38 // Overrides SafeBrowsingService::CheckBrowseUrl. 39 virtual bool CheckBrowseUrl(const GURL& gurl, Client* client) { 40 if (badurls[gurl.spec()] == SAFE) 41 return true; 42 43 BrowserThread::PostTask( 44 BrowserThread::IO, FROM_HERE, 45 NewRunnableMethod(this, &FakeSafeBrowsingService::OnCheckBrowseURLDone, 46 gurl, client)); 47 return false; 48 } 49 50 void OnCheckBrowseURLDone(const GURL& gurl, Client* client) { 51 SafeBrowsingService::SafeBrowsingCheck check; 52 check.urls.push_back(gurl); 53 check.client = client; 54 check.result = badurls[gurl.spec()]; 55 client->OnSafeBrowsingResult(check); 56 } 57 58 void AddURLResult(const GURL& url, UrlCheckResult checkresult) { 59 badurls[url.spec()] = checkresult; 60 } 61 62 // Overrides SafeBrowsingService. 63 virtual void SendSerializedMalwareDetails(const std::string& serialized) { 64 reports_.push_back(serialized); 65 // Notify the UI thread that we got a report. 66 BrowserThread::PostTask( 67 BrowserThread::UI, FROM_HERE, 68 NewRunnableMethod(this, 69 &FakeSafeBrowsingService::OnMalwareDetailsDone)); 70 } 71 72 void OnMalwareDetailsDone() { 73 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 74 MessageLoopForUI::current()->Quit(); 75 } 76 77 std::string GetReport() { 78 EXPECT_TRUE(reports_.size() == 1); 79 return reports_[0]; 80 } 81 82 std::vector<std::string> reports_; 83 84 private: 85 base::hash_map<std::string, UrlCheckResult> badurls; 86 }; 87 88 // Factory that creates FakeSafeBrowsingService instances. 89 class TestSafeBrowsingServiceFactory : public SafeBrowsingServiceFactory { 90 public: 91 TestSafeBrowsingServiceFactory() { } 92 virtual ~TestSafeBrowsingServiceFactory() { } 93 94 virtual SafeBrowsingService* CreateSafeBrowsingService() { 95 return new FakeSafeBrowsingService(); 96 } 97 }; 98 99 // A MalwareDetails class lets us intercept calls from the renderer. 100 class FakeMalwareDetails : public MalwareDetails { 101 public: 102 FakeMalwareDetails(SafeBrowsingService* sb_service, 103 TabContents* tab_contents, 104 const SafeBrowsingService::UnsafeResource& unsafe_resource) 105 : MalwareDetails(sb_service, tab_contents, unsafe_resource) { } 106 107 virtual ~FakeMalwareDetails() {} 108 109 virtual void AddDOMDetails( 110 const std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node>& params) { 111 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 112 MalwareDetails::AddDOMDetails(params); 113 114 // Notify the UI thread that we got the dom details. 115 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 116 NewRunnableMethod(this, 117 &FakeMalwareDetails::OnDOMDetailsDone)); 118 } 119 120 void OnDOMDetailsDone() { 121 got_dom_ = true; 122 if (waiting_) { 123 MessageLoopForUI::current()->Quit(); 124 } 125 } 126 127 bool got_dom() const { 128 return got_dom_; 129 } 130 131 bool waiting() const { 132 return waiting_; 133 } 134 135 void set_got_dom(bool got_dom) { 136 got_dom_ = got_dom; 137 } 138 139 void set_waiting(bool waiting) { 140 waiting_ = waiting; 141 } 142 143 safe_browsing::ClientMalwareReportRequest* get_report() { 144 return report_.get(); 145 } 146 147 private: 148 // Some logic to figure out if we should wait for the dom details or not. 149 // These variables should only be accessed in the UI thread. 150 bool got_dom_; 151 bool waiting_; 152 153 }; 154 155 class TestMalwareDetailsFactory : public MalwareDetailsFactory { 156 public: 157 TestMalwareDetailsFactory() { } 158 virtual ~TestMalwareDetailsFactory() { } 159 160 virtual MalwareDetails* CreateMalwareDetails( 161 SafeBrowsingService* sb_service, 162 TabContents* tab_contents, 163 const SafeBrowsingService::UnsafeResource& unsafe_resource) { 164 details_ = new FakeMalwareDetails(sb_service, tab_contents, 165 unsafe_resource); 166 return details_; 167 } 168 169 FakeMalwareDetails* get_details() { 170 return details_; 171 } 172 173 private: 174 FakeMalwareDetails* details_; 175 }; 176 177 // A SafeBrowingBlockingPage class that lets us wait until it's hidden. 178 class TestSafeBrowsingBlockingPage : public SafeBrowsingBlockingPage { 179 public: 180 TestSafeBrowsingBlockingPage(SafeBrowsingService* service, 181 TabContents* tab_contents, 182 const UnsafeResourceList& unsafe_resources) 183 : SafeBrowsingBlockingPage(service, tab_contents, unsafe_resources) { 184 wait_for_delete_ = false; 185 } 186 187 ~TestSafeBrowsingBlockingPage() { 188 if (wait_for_delete_) { 189 // Notify that we are gone 190 MessageLoopForUI::current()->Quit(); 191 } 192 } 193 194 void set_wait_for_delete() { 195 wait_for_delete_ = true; 196 } 197 198 private: 199 bool wait_for_delete_; 200 }; 201 202 class TestSafeBrowsingBlockingPageFactory 203 : public SafeBrowsingBlockingPageFactory { 204 public: 205 TestSafeBrowsingBlockingPageFactory() { } 206 ~TestSafeBrowsingBlockingPageFactory() { } 207 208 virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage( 209 SafeBrowsingService* service, 210 TabContents* tab_contents, 211 const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) { 212 return new TestSafeBrowsingBlockingPage(service, tab_contents, 213 unsafe_resources); 214 } 215 }; 216 217 // Tests the safe browsing blocking page in a browser. 218 class SafeBrowsingBlockingPageTest : public InProcessBrowserTest, 219 public SafeBrowsingService::Client { 220 public: 221 SafeBrowsingBlockingPageTest() { 222 } 223 224 virtual void SetUp() { 225 SafeBrowsingService::RegisterFactory(&factory_); 226 SafeBrowsingBlockingPage::RegisterFactory(&blocking_page_factory_); 227 MalwareDetails::RegisterFactory(&details_factory_); 228 InProcessBrowserTest::SetUp(); 229 } 230 231 virtual void TearDown() { 232 InProcessBrowserTest::TearDown(); 233 SafeBrowsingBlockingPage::RegisterFactory(NULL); 234 SafeBrowsingService::RegisterFactory(NULL); 235 MalwareDetails::RegisterFactory(NULL); 236 } 237 238 virtual void SetUpInProcessBrowserTestFixture() { 239 ASSERT_TRUE(test_server()->Start()); 240 } 241 242 // SafeBrowsingService::Client implementation. 243 virtual void OnSafeBrowsingResult( 244 const SafeBrowsingService::SafeBrowsingCheck& check) { 245 } 246 247 virtual void OnBlockingPageComplete(bool proceed) { 248 } 249 250 void AddURLResult(const GURL& url, 251 SafeBrowsingService::UrlCheckResult checkresult) { 252 FakeSafeBrowsingService* service = 253 static_cast<FakeSafeBrowsingService*>( 254 g_browser_process->resource_dispatcher_host()-> 255 safe_browsing_service()); 256 257 ASSERT_TRUE(service); 258 service->AddURLResult(url, checkresult); 259 } 260 261 void SendCommand(const std::string& command) { 262 TabContents* contents = browser()->GetSelectedTabContents(); 263 // We use InterstitialPage::GetInterstitialPage(tab) instead of 264 // tab->interstitial_page() because the tab doesn't have a pointer 265 // to its interstital page until it gets a command from the renderer 266 // that it has indeed displayed it -- and this sometimes happens after 267 // NavigateToURL returns. 268 SafeBrowsingBlockingPage* interstitial_page = 269 static_cast<SafeBrowsingBlockingPage*>( 270 InterstitialPage::GetInterstitialPage(contents)); 271 ASSERT_TRUE(interstitial_page); 272 interstitial_page->CommandReceived(command); 273 } 274 275 void DontProceedThroughInterstitial() { 276 TabContents* contents = browser()->GetSelectedTabContents(); 277 InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage( 278 contents); 279 ASSERT_TRUE(interstitial_page); 280 interstitial_page->DontProceed(); 281 } 282 283 void ProceedThroughInterstitial() { 284 TabContents* contents = browser()->GetSelectedTabContents(); 285 InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage( 286 contents); 287 ASSERT_TRUE(interstitial_page); 288 interstitial_page->Proceed(); 289 } 290 291 void AssertNoInterstitial(bool wait_for_delete) { 292 TabContents* contents = browser()->GetSelectedTabContents(); 293 294 if (contents->showing_interstitial_page() && wait_for_delete) { 295 // We'll get notified when the interstitial is deleted. 296 static_cast<TestSafeBrowsingBlockingPage*>( 297 contents->interstitial_page())->set_wait_for_delete(); 298 ui_test_utils::RunMessageLoop(); 299 } 300 301 // Can't use InterstitialPage::GetInterstitialPage() because that 302 // gets updated after the TestSafeBrowsingBlockingPage destructor 303 ASSERT_FALSE(contents->showing_interstitial_page()); 304 } 305 306 bool YesInterstitial() { 307 TabContents* contents = browser()->GetSelectedTabContents(); 308 InterstitialPage* interstitial_page = InterstitialPage::GetInterstitialPage( 309 contents); 310 return interstitial_page != NULL; 311 } 312 313 void WaitForInterstitial() { 314 TabContents* contents = browser()->GetSelectedTabContents(); 315 if (!InterstitialPage::GetInterstitialPage(contents)) 316 ui_test_utils::WaitForNotificationFrom( 317 NotificationType::INTERSTITIAL_ATTACHED, 318 Source<TabContents>(contents)); 319 } 320 321 void WaitForNavigation() { 322 NavigationController* controller = 323 &browser()->GetSelectedTabContents()->controller(); 324 ui_test_utils::WaitForNavigation(controller); 325 } 326 327 void AssertReportSent() { 328 // When a report is scheduled in the IO thread we should get notified. 329 ui_test_utils::RunMessageLoop(); 330 331 FakeSafeBrowsingService* service = 332 static_cast<FakeSafeBrowsingService*>( 333 g_browser_process->resource_dispatcher_host()-> 334 safe_browsing_service()); 335 336 std::string serialized = service->GetReport(); 337 338 safe_browsing::ClientMalwareReportRequest report; 339 ASSERT_TRUE(report.ParseFromString(serialized)); 340 341 // Verify the report is complete. 342 EXPECT_TRUE(report.complete()); 343 } 344 345 void MalwareRedirectCancelAndProceed(const std::string open_function); 346 347 protected: 348 TestMalwareDetailsFactory details_factory_; 349 350 private: 351 TestSafeBrowsingServiceFactory factory_; 352 TestSafeBrowsingBlockingPageFactory blocking_page_factory_; 353 354 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageTest); 355 }; 356 357 void SafeBrowsingBlockingPageTest::MalwareRedirectCancelAndProceed( 358 const std::string open_function) { 359 GURL load_url = test_server()->GetURL( 360 "files/safe_browsing/interstitial_cancel.html"); 361 GURL malware_url("http://localhost/files/safe_browsing/malware.html"); 362 AddURLResult(malware_url, SafeBrowsingService::URL_MALWARE); 363 364 // Load the test page. 365 ui_test_utils::NavigateToURL(browser(), load_url); 366 // Trigger the safe browsing interstitial page via a redirect in "openWin()". 367 ui_test_utils::NavigateToURLWithDisposition( 368 browser(), 369 GURL("javascript:" + open_function + "()"), 370 CURRENT_TAB, 371 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 372 WaitForInterstitial(); 373 // Cancel the redirect request while interstitial page is open. 374 browser()->ActivateTabAt(0, true); 375 ui_test_utils::NavigateToURLWithDisposition( 376 browser(), 377 GURL("javascript:stopWin()"), 378 CURRENT_TAB, 379 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 380 browser()->ActivateTabAt(1, true); 381 // Simulate the user clicking "proceed", there should be no crash. 382 SendCommand("\"proceed\""); 383 } 384 385 namespace { 386 387 const char kEmptyPage[] = "files/empty.html"; 388 const char kMalwarePage[] = "files/safe_browsing/malware.html"; 389 const char kMalwareIframe[] = "files/safe_browsing/malware_iframe.html"; 390 391 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, 392 MalwareRedirectInIFrameCanceled) { 393 // 1. Test the case that redirect is a subresource. 394 MalwareRedirectCancelAndProceed("openWinIFrame"); 395 // If the redirect was from subresource but canceled, "proceed" will continue 396 // with the rest of resources. 397 AssertNoInterstitial(true); 398 } 399 400 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, MalwareRedirectCanceled) { 401 // 2. Test the case that redirect is the only resource. 402 MalwareRedirectCancelAndProceed("openWin"); 403 // Clicking proceed won't do anything if the main request is cancelled 404 // already. See crbug.com/76460. 405 EXPECT_TRUE(YesInterstitial()); 406 } 407 408 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, MalwareDontProceed) { 409 GURL url = test_server()->GetURL(kEmptyPage); 410 AddURLResult(url, SafeBrowsingService::URL_MALWARE); 411 412 ui_test_utils::NavigateToURL(browser(), url); 413 414 SendCommand("\"takeMeBack\""); // Simulate the user clicking "back" 415 AssertNoInterstitial(false); // Assert the interstitial is gone 416 EXPECT_EQ(GURL(chrome::kAboutBlankURL), // Back to "about:blank" 417 browser()->GetSelectedTabContents()->GetURL()); 418 } 419 420 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, MalwareProceed) { 421 GURL url = test_server()->GetURL(kEmptyPage); 422 AddURLResult(url, SafeBrowsingService::URL_MALWARE); 423 424 ui_test_utils::NavigateToURL(browser(), url); 425 SendCommand("\"proceed\""); // Simulate the user clicking "proceed" 426 WaitForNavigation(); // Wait until we finish the navigation. 427 AssertNoInterstitial(true); // Assert the interstitial is gone. 428 EXPECT_EQ(url, browser()->GetSelectedTabContents()->GetURL()); 429 } 430 431 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, PhishingDontProceed) { 432 GURL url = test_server()->GetURL(kEmptyPage); 433 AddURLResult(url, SafeBrowsingService::URL_PHISHING); 434 435 ui_test_utils::NavigateToURL(browser(), url); 436 437 SendCommand("\"takeMeBack\""); // Simulate the user clicking "proceed" 438 AssertNoInterstitial(false); // Assert the interstitial is gone 439 EXPECT_EQ(GURL(chrome::kAboutBlankURL), // We are back to "about:blank". 440 browser()->GetSelectedTabContents()->GetURL()); 441 } 442 443 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, PhishingProceed) { 444 GURL url = test_server()->GetURL(kEmptyPage); 445 AddURLResult(url, SafeBrowsingService::URL_PHISHING); 446 447 ui_test_utils::NavigateToURL(browser(), url); 448 449 SendCommand("\"proceed\""); // Simulate the user clicking "proceed". 450 WaitForNavigation(); // Wait until we finish the navigation. 451 AssertNoInterstitial(true); // Assert the interstitial is gone 452 EXPECT_EQ(url, browser()->GetSelectedTabContents()->GetURL()); 453 } 454 455 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, PhishingReportError) { 456 GURL url = test_server()->GetURL(kEmptyPage); 457 AddURLResult(url, SafeBrowsingService::URL_PHISHING); 458 459 ui_test_utils::NavigateToURL(browser(), url); 460 461 SendCommand("\"reportError\""); // Simulate the user clicking "report error" 462 WaitForNavigation(); // Wait until we finish the navigation. 463 AssertNoInterstitial(false); // Assert the interstitial is gone 464 465 // We are in the error reporting page. 466 EXPECT_EQ("/safebrowsing/report_error/", 467 browser()->GetSelectedTabContents()->GetURL().path()); 468 } 469 470 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, PhishingLearnMore) { 471 GURL url = test_server()->GetURL(kEmptyPage); 472 AddURLResult(url, SafeBrowsingService::URL_PHISHING); 473 474 ui_test_utils::NavigateToURL(browser(), url); 475 476 SendCommand("\"learnMore\""); // Simulate the user clicking "learn more" 477 WaitForNavigation(); // Wait until we finish the navigation. 478 AssertNoInterstitial(false); // Assert the interstitial is gone 479 480 // We are in the help page. 481 EXPECT_EQ("/support/bin/answer.py", 482 browser()->GetSelectedTabContents()->GetURL().path()); 483 } 484 485 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, MalwareIframeDontProceed) { 486 GURL url = test_server()->GetURL(kMalwarePage); 487 GURL iframe_url = test_server()->GetURL(kMalwareIframe); 488 AddURLResult(iframe_url, SafeBrowsingService::URL_MALWARE); 489 490 ui_test_utils::NavigateToURL(browser(), url); 491 492 SendCommand("\"takeMeBack\""); // Simulate the user clicking "back" 493 AssertNoInterstitial(false); // Assert the interstitial is gone 494 495 EXPECT_EQ(GURL(chrome::kAboutBlankURL), // Back to "about:blank" 496 browser()->GetSelectedTabContents()->GetURL()); 497 } 498 499 // Crashy, http://crbug.com/68834. 500 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, 501 DISABLED_MalwareIframeProceed) { 502 GURL url = test_server()->GetURL(kMalwarePage); 503 GURL iframe_url = test_server()->GetURL(kMalwareIframe); 504 AddURLResult(iframe_url, SafeBrowsingService::URL_MALWARE); 505 506 ui_test_utils::NavigateToURL(browser(), url); 507 508 SendCommand("\"proceed\""); // Simulate the user clicking "proceed" 509 AssertNoInterstitial(true); // Assert the interstitial is gone 510 511 EXPECT_EQ(url, browser()->GetSelectedTabContents()->GetURL()); 512 } 513 514 IN_PROC_BROWSER_TEST_F(SafeBrowsingBlockingPageTest, 515 MalwareIframeReportDetails) { 516 GURL url = test_server()->GetURL(kMalwarePage); 517 GURL iframe_url = test_server()->GetURL(kMalwareIframe); 518 AddURLResult(iframe_url, SafeBrowsingService::URL_MALWARE); 519 520 ui_test_utils::NavigateToURL(browser(), url); 521 522 // If the DOM details from renderer did not already return, wait for them. 523 if (!details_factory_.get_details()->got_dom()) { 524 // This condition might not trigger normally, but if you add a 525 // sleep(1) in malware_dom_details it triggers :). 526 details_factory_.get_details()->set_waiting(true); 527 LOG(INFO) << "Waiting for dom details."; 528 ui_test_utils::RunMessageLoop(); 529 } else { 530 LOG(INFO) << "Already got the dom details."; 531 } 532 533 SendCommand("\"doReport\""); // Simulate the user checking the checkbox. 534 EXPECT_TRUE(browser()->GetProfile()->GetPrefs()->GetBoolean( 535 prefs::kSafeBrowsingReportingEnabled)); 536 537 SendCommand("\"proceed\""); // Simulate the user clicking "back" 538 AssertNoInterstitial(true); // Assert the interstitial is gone 539 540 EXPECT_EQ(url, browser()->GetSelectedTabContents()->GetURL()); 541 AssertReportSent(); 542 } 543 544 } // namespace 545