safe_browsing_blocking_page_unittest.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/renderer_host/test/test_render_view_host.h"
6
7#include "chrome/browser/chrome_thread.h"
8#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
9#include "chrome/browser/tab_contents/navigation_entry.h"
10#include "chrome/browser/tab_contents/test_tab_contents.h"
11#include "chrome/common/render_messages.h"
12#include "chrome/common/render_messages_params.h"
13
14static const char* kGoogleURL = "http://www.google.com/";
15static const char* kGoodURL = "http://www.goodguys.com/";
16static const char* kBadURL = "http://www.badguys.com/";
17static const char* kBadURL2 = "http://www.badguys2.com/";
18static const char* kBadURL3 = "http://www.badguys3.com/";
19
20// A SafeBrowingBlockingPage class that does not create windows.
21class TestSafeBrowsingBlockingPage :  public SafeBrowsingBlockingPage {
22 public:
23  TestSafeBrowsingBlockingPage(SafeBrowsingService* service,
24                               TabContents* tab_contents,
25                               const UnsafeResourceList& unsafe_resources)
26      : SafeBrowsingBlockingPage(service, tab_contents, unsafe_resources) {
27  }
28
29  // Overriden from InterstitialPage.  Don't create a view.
30  virtual TabContentsView* CreateTabContentsView() {
31    return NULL;
32  }
33};
34
35class TestSafeBrowsingBlockingPageFactory
36    : public SafeBrowsingBlockingPageFactory {
37 public:
38  TestSafeBrowsingBlockingPageFactory() { }
39  ~TestSafeBrowsingBlockingPageFactory() { }
40
41  virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
42      SafeBrowsingService* service,
43      TabContents* tab_contents,
44      const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) {
45    return new TestSafeBrowsingBlockingPage(service, tab_contents,
46                                            unsafe_resources);
47  }
48};
49
50class SafeBrowsingBlockingPageTest : public RenderViewHostTestHarness,
51                                     public SafeBrowsingService::Client {
52 public:
53  // The decision the user made.
54  enum UserResponse {
55    PENDING,
56    OK,
57    CANCEL
58  };
59
60  SafeBrowsingBlockingPageTest()
61      : ui_thread_(ChromeThread::UI, MessageLoop::current()),
62        io_thread_(ChromeThread::IO, MessageLoop::current()) {
63    ResetUserResponse();
64    service_ = new SafeBrowsingService();
65  }
66
67  virtual void SetUp() {
68    RenderViewHostTestHarness::SetUp();
69    SafeBrowsingBlockingPage::RegisterFactory(&factory_);
70    ResetUserResponse();
71  }
72
73  // SafeBrowsingService::Client implementation.
74  virtual void OnUrlCheckResult(const GURL& url,
75                                SafeBrowsingService::UrlCheckResult result) {
76  }
77  virtual void OnBlockingPageComplete(bool proceed) {
78    if (proceed)
79      user_response_ = OK;
80    else
81      user_response_ = CANCEL;
82  }
83
84  void Navigate(const char* url, int page_id) {
85    ViewHostMsg_FrameNavigate_Params params;
86    InitNavigateParams(&params, page_id, GURL(url), PageTransition::TYPED);
87    contents()->TestDidNavigate(contents_->render_view_host(), params);
88  }
89
90  void GoBack() {
91    NavigationEntry* entry = contents()->controller().GetEntryAtOffset(-1);
92    ASSERT_TRUE(entry);
93    contents()->controller().GoBack();
94    Navigate(entry->url().spec().c_str(), entry->page_id());
95  }
96
97  void ShowInterstitial(ResourceType::Type resource_type,
98                        const char* url) {
99    SafeBrowsingService::UnsafeResource resource;
100    InitResource(&resource, resource_type, GURL(url));
101    SafeBrowsingBlockingPage::ShowBlockingPage(service_, resource);
102  }
103
104  // Returns the SafeBrowsingBlockingPage currently showing or NULL if none is
105  // showing.
106  SafeBrowsingBlockingPage* GetSafeBrowsingBlockingPage() {
107    InterstitialPage* interstitial =
108        InterstitialPage::GetInterstitialPage(contents());
109    if (!interstitial)
110      return NULL;
111    return  static_cast<SafeBrowsingBlockingPage*>(interstitial);
112  }
113
114  UserResponse user_response() const { return user_response_; }
115  void ResetUserResponse() { user_response_ = PENDING; }
116
117  static void ProceedThroughInterstitial(
118      SafeBrowsingBlockingPage* sb_interstitial) {
119    sb_interstitial->Proceed();
120    // Proceed() posts a task to update the SafeBrowsingService::Client.
121    MessageLoop::current()->RunAllPending();
122  }
123
124  static void DontProceedThroughInterstitial(
125      SafeBrowsingBlockingPage* sb_interstitial) {
126    sb_interstitial->DontProceed();
127    // DontProceed() posts a task to update the SafeBrowsingService::Client.
128    MessageLoop::current()->RunAllPending();
129  }
130
131 private:
132  void InitResource(SafeBrowsingService::UnsafeResource* resource,
133                    ResourceType::Type resource_type,
134                    const GURL& url) {
135    resource->client = this;
136    resource->url = url;
137    resource->resource_type = resource_type;
138    resource->threat_type = SafeBrowsingService::URL_MALWARE;
139    resource->render_process_host_id = contents_->GetRenderProcessHost()->id();
140    resource->render_view_id = contents_->render_view_host()->routing_id();
141  }
142
143  UserResponse user_response_;
144  scoped_refptr<SafeBrowsingService> service_;
145  TestSafeBrowsingBlockingPageFactory factory_;
146  ChromeThread ui_thread_;
147  ChromeThread io_thread_;
148};
149
150// Tests showing a blocking page for a malware page and not proceeding.
151TEST_F(SafeBrowsingBlockingPageTest, MalwarePageDontProceed) {
152  // Start a load.
153  controller().LoadURL(GURL(kBadURL), GURL(), PageTransition::TYPED);
154
155  // Simulate the load causing a safe browsing interstitial to be shown.
156  ShowInterstitial(ResourceType::MAIN_FRAME, kBadURL);
157  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
158  ASSERT_TRUE(sb_interstitial);
159
160  MessageLoop::current()->RunAllPending();
161
162  // Simulate the user clicking "don't proceed".
163  DontProceedThroughInterstitial(sb_interstitial);
164
165  // The interstitial should be gone.
166  EXPECT_EQ(CANCEL, user_response());
167  EXPECT_FALSE(GetSafeBrowsingBlockingPage());
168
169  // We did not proceed, the pending entry should be gone.
170  EXPECT_FALSE(controller().pending_entry());
171}
172
173// Tests showing a blocking page for a malware page and then proceeding.
174TEST_F(SafeBrowsingBlockingPageTest, MalwarePageProceed) {
175  // Start a load.
176  controller().LoadURL(GURL(kBadURL), GURL(), PageTransition::TYPED);
177
178  // Simulate the load causing a safe browsing interstitial to be shown.
179  ShowInterstitial(ResourceType::MAIN_FRAME, kBadURL);
180  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
181  ASSERT_TRUE(sb_interstitial);
182
183  // Simulate the user clicking "proceed".
184  ProceedThroughInterstitial(sb_interstitial);
185
186  // The interstitial is shown until the navigation commits.
187  ASSERT_TRUE(InterstitialPage::GetInterstitialPage(contents()));
188  // Commit the navigation.
189  Navigate(kBadURL, 1);
190  // The interstitial should be gone now.
191  ASSERT_FALSE(InterstitialPage::GetInterstitialPage(contents()));
192}
193
194// Tests showing a blocking page for a page that contains malware subresources
195// and not proceeding.
196TEST_F(SafeBrowsingBlockingPageTest, PageWithMalwareResourceDontProceed) {
197  // Navigate somewhere.
198  Navigate(kGoogleURL, 1);
199
200  // Navigate somewhere else.
201  Navigate(kGoodURL, 2);
202
203  // Simulate that page loading a bad-resource triggering an interstitial.
204  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL);
205
206  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
207  ASSERT_TRUE(sb_interstitial);
208
209  // Simulate the user clicking "don't proceed".
210  DontProceedThroughInterstitial(sb_interstitial);
211  EXPECT_EQ(CANCEL, user_response());
212  EXPECT_FALSE(GetSafeBrowsingBlockingPage());
213
214  // We did not proceed, we should be back to the first page, the 2nd one should
215  // have been removed from the navigation controller.
216  ASSERT_EQ(1, controller().entry_count());
217  EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->url().spec());
218}
219
220// Tests showing a blocking page for a page that contains malware subresources
221// and proceeding.
222TEST_F(SafeBrowsingBlockingPageTest, PageWithMalwareResourceProceed) {
223  // Navigate somewhere.
224  Navigate(kGoodURL, 1);
225
226  // Simulate that page loading a bad-resource triggering an interstitial.
227  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL);
228
229  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
230  ASSERT_TRUE(sb_interstitial);
231
232  // Simulate the user clicking "proceed".
233  ProceedThroughInterstitial(sb_interstitial);
234  EXPECT_EQ(OK, user_response());
235  EXPECT_FALSE(GetSafeBrowsingBlockingPage());
236
237  // We did proceed, we should be back to showing the page.
238  ASSERT_EQ(1, controller().entry_count());
239  EXPECT_EQ(kGoodURL, controller().GetActiveEntry()->url().spec());
240}
241
242// Tests showing a blocking page for a page that contains multiple malware
243// subresources and not proceeding.  This just tests that the extra malware
244// subresources (which trigger queued interstitial pages) do not break anything.
245TEST_F(SafeBrowsingBlockingPageTest,
246       PageWithMultipleMalwareResourceDontProceed) {
247  // Navigate somewhere.
248  Navigate(kGoogleURL, 1);
249
250  // Navigate somewhere else.
251  Navigate(kGoodURL, 2);
252
253  // Simulate that page loading a bad-resource triggering an interstitial.
254  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL);
255
256  // More bad resources loading causing more interstitials. The new
257  // interstitials should be queued.
258  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL2);
259  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL3);
260
261  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
262  ASSERT_TRUE(sb_interstitial);
263
264  // Simulate the user clicking "don't proceed".
265  DontProceedThroughInterstitial(sb_interstitial);
266  EXPECT_EQ(CANCEL, user_response());
267  EXPECT_FALSE(GetSafeBrowsingBlockingPage());
268
269  // We did not proceed, we should be back to the first page, the 2nd one should
270  // have been removed from the navigation controller.
271  ASSERT_EQ(1, controller().entry_count());
272  EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->url().spec());
273}
274
275// Tests showing a blocking page for a page that contains multiple malware
276// subresources and proceeding through the first interstitial, but not the next.
277TEST_F(SafeBrowsingBlockingPageTest,
278       PageWithMultipleMalwareResourceProceedThenDontProceed) {
279  // Navigate somewhere.
280  Navigate(kGoogleURL, 1);
281
282  // Navigate somewhere else.
283  Navigate(kGoodURL, 2);
284
285  // Simulate that page loading a bad-resource triggering an interstitial.
286  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL);
287
288  // More bad resources loading causing more interstitials. The new
289  // interstitials should be queued.
290  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL2);
291  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL3);
292
293  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
294  ASSERT_TRUE(sb_interstitial);
295
296  // Proceed through the 1st interstitial.
297  ProceedThroughInterstitial(sb_interstitial);
298  EXPECT_EQ(OK, user_response());
299
300  ResetUserResponse();
301
302  // We should land to a 2nd interstitial (aggregating all the malware resources
303  // loaded while the 1st interstitial was showing).
304  sb_interstitial = GetSafeBrowsingBlockingPage();
305  ASSERT_TRUE(sb_interstitial);
306
307  // Don't proceed through the 2nd interstitial.
308  DontProceedThroughInterstitial(sb_interstitial);
309  EXPECT_EQ(CANCEL, user_response());
310  EXPECT_FALSE(GetSafeBrowsingBlockingPage());
311
312  // We did not proceed, we should be back to the first page, the 2nd one should
313  // have been removed from the navigation controller.
314  ASSERT_EQ(1, controller().entry_count());
315  EXPECT_EQ(kGoogleURL, controller().GetActiveEntry()->url().spec());
316}
317
318// Tests showing a blocking page for a page that contains multiple malware
319// subresources and proceeding through the multiple interstitials.
320TEST_F(SafeBrowsingBlockingPageTest, PageWithMultipleMalwareResourceProceed) {
321  // Navigate somewhere else.
322  Navigate(kGoodURL, 1);
323
324  // Simulate that page loading a bad-resource triggering an interstitial.
325  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL);
326
327  // More bad resources loading causing more interstitials. The new
328  // interstitials should be queued.
329  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL2);
330  ShowInterstitial(ResourceType::SUB_RESOURCE, kBadURL3);
331
332  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
333  ASSERT_TRUE(sb_interstitial);
334
335  // Proceed through the 1st interstitial.
336  ProceedThroughInterstitial(sb_interstitial);
337  EXPECT_EQ(OK, user_response());
338
339  ResetUserResponse();
340
341  // We should land to a 2nd interstitial (aggregating all the malware resources
342  // loaded while the 1st interstitial was showing).
343  sb_interstitial = GetSafeBrowsingBlockingPage();
344  ASSERT_TRUE(sb_interstitial);
345
346  // Proceed through the 2nd interstitial.
347  ProceedThroughInterstitial(sb_interstitial);
348  EXPECT_EQ(OK, user_response());
349
350  // We did proceed, we should be back to the initial page.
351  ASSERT_EQ(1, controller().entry_count());
352  EXPECT_EQ(kGoodURL, controller().GetActiveEntry()->url().spec());
353}
354
355// Tests showing a blocking page then navigating back and forth to make sure the
356// controller entries are OK.  http://crbug.com/17627
357TEST_F(SafeBrowsingBlockingPageTest, NavigatingBackAndForth) {
358  // Navigate somewhere.
359  Navigate(kGoodURL, 1);
360
361  // Now navigate to a bad page triggerring an interstitial.
362  controller().LoadURL(GURL(kBadURL), GURL(), PageTransition::TYPED);
363  ShowInterstitial(ResourceType::MAIN_FRAME, kBadURL);
364  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
365  ASSERT_TRUE(sb_interstitial);
366
367  // Proceed, then navigate back.
368  ProceedThroughInterstitial(sb_interstitial);
369  Navigate(kBadURL, 2);  // Commit the navigation.
370  GoBack();
371
372  // We are back on the good page.
373  sb_interstitial = GetSafeBrowsingBlockingPage();
374  ASSERT_FALSE(sb_interstitial);
375  ASSERT_EQ(2, controller().entry_count());
376  EXPECT_EQ(kGoodURL, controller().GetActiveEntry()->url().spec());
377
378  // Navigate forward to the malware URL.
379  contents()->controller().GoForward();
380  ShowInterstitial(ResourceType::MAIN_FRAME, kBadURL);
381  sb_interstitial = GetSafeBrowsingBlockingPage();
382  ASSERT_TRUE(sb_interstitial);
383
384  // Let's proceed and make sure everything is OK (bug 17627).
385  ProceedThroughInterstitial(sb_interstitial);
386  Navigate(kBadURL, 2);  // Commit the navigation.
387  sb_interstitial = GetSafeBrowsingBlockingPage();
388  ASSERT_FALSE(sb_interstitial);
389  ASSERT_EQ(2, controller().entry_count());
390  EXPECT_EQ(kBadURL, controller().GetActiveEntry()->url().spec());
391}
392
393// Tests that calling "don't proceed" after "proceed" has been called doesn't
394// cause problems. http://crbug.com/30079
395TEST_F(SafeBrowsingBlockingPageTest, ProceedThenDontProceed) {
396  // Start a load.
397  controller().LoadURL(GURL(kBadURL), GURL(), PageTransition::TYPED);
398
399  // Simulate the load causing a safe browsing interstitial to be shown.
400  ShowInterstitial(ResourceType::MAIN_FRAME, kBadURL);
401  SafeBrowsingBlockingPage* sb_interstitial = GetSafeBrowsingBlockingPage();
402  ASSERT_TRUE(sb_interstitial);
403
404  MessageLoop::current()->RunAllPending();
405
406  // Simulate the user clicking "proceed" then "don't proceed" (before the
407  // interstitial is shown).
408  sb_interstitial->Proceed();
409  sb_interstitial->DontProceed();
410  // Proceed() and DontProceed() post a task to update the
411  // SafeBrowsingService::Client.
412  MessageLoop::current()->RunAllPending();
413
414  // The interstitial should be gone.
415  EXPECT_EQ(OK, user_response());
416  EXPECT_FALSE(GetSafeBrowsingBlockingPage());
417}
418