Home | History | Annotate | Download | only in safe_browsing
      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