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.
28class 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.
89class 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.
100class 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
155class 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.
178class 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
202class 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.
218class 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
357void 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
385namespace {
386
387const char kEmptyPage[] = "files/empty.html";
388const char kMalwarePage[] = "files/safe_browsing/malware.html";
389const char kMalwareIframe[] = "files/safe_browsing/malware_iframe.html";
390
391IN_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
400IN_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
408IN_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
420IN_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
431IN_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
443IN_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
455IN_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
470IN_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
485IN_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.
500IN_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
514IN_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