Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / safe_browsing / client_side_detection_host_unittest.cc
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 #include "base/files/file_path.h"
6 #include "base/memory/ref_counted.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/run_loop.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
12 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
13 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
14 #include "chrome/browser/safe_browsing/database_manager.h"
15 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
16 #include "chrome/browser/safe_browsing/ui_manager.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/safe_browsing/csd.pb.h"
19 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
20 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "content/public/browser/navigation_entry.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/mock_render_process_host.h"
25 #include "content/public/test/test_browser_thread.h"
26 #include "content/public/test/test_renderer_host.h"
27 #include "content/public/test/web_contents_tester.h"
28 #include "ipc/ipc_test_sink.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "url/gurl.h"
32
33 using ::testing::_;
34 using ::testing::DeleteArg;
35 using ::testing::DoAll;
36 using ::testing::Eq;
37 using ::testing::IsNull;
38 using ::testing::Mock;
39 using ::testing::NiceMock;
40 using ::testing::NotNull;
41 using ::testing::Pointee;
42 using ::testing::Return;
43 using ::testing::SaveArg;
44 using ::testing::SetArgumentPointee;
45 using ::testing::StrictMock;
46 using content::BrowserThread;
47 using content::RenderViewHostTester;
48 using content::ResourceType;
49 using content::WebContents;
50
51 namespace {
52
53 const bool kFalse = false;
54 const bool kTrue = true;
55
56 }  // namespace
57
58 namespace safe_browsing {
59 namespace {
60 // This matcher verifies that the client computed verdict
61 // (ClientPhishingRequest) which is passed to SendClientReportPhishingRequest
62 // has the expected fields set.  Note: we can't simply compare the protocol
63 // buffer strings because the BrowserFeatureExtractor might add features to the
64 // verdict object before calling SendClientReportPhishingRequest.
65 MATCHER_P(PartiallyEqualVerdict, other, "") {
66   return (other.url() == arg.url() &&
67           other.client_score() == arg.client_score() &&
68           other.is_phishing() == arg.is_phishing());
69 }
70
71 MATCHER_P(PartiallyEqualMalwareVerdict, other, "") {
72   if (other.url() != arg.url() ||
73       other.referrer_url() != arg.referrer_url() ||
74       other.bad_ip_url_info_size() != arg.bad_ip_url_info_size())
75     return false;
76
77   for (int i = 0; i < other.bad_ip_url_info_size(); ++i) {
78     if (other.bad_ip_url_info(i).ip() != arg.bad_ip_url_info(i).ip() ||
79         other.bad_ip_url_info(i).url() != arg.bad_ip_url_info(i).url())
80     return false;
81   }
82   return true;
83 }
84
85 // Test that the callback is NULL when the verdict is not phishing.
86 MATCHER(CallbackIsNull, "") {
87   return arg.is_null();
88 }
89
90 ACTION(QuitUIMessageLoop) {
91   EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
92   base::MessageLoopForUI::current()->Quit();
93 }
94
95 ACTION_P(InvokeDoneCallback, verdict) {
96   scoped_ptr<ClientPhishingRequest> request(::std::tr1::get<1>(args));
97   request->CopyFrom(*verdict);
98   ::std::tr1::get<2>(args).Run(true, request.Pass());
99 }
100
101 ACTION_P(InvokeMalwareCallback, verdict) {
102   scoped_ptr<ClientMalwareRequest> request(::std::tr1::get<1>(args));
103   request->CopyFrom(*verdict);
104   ::std::tr1::get<2>(args).Run(true, request.Pass());
105 }
106
107 void EmptyUrlCheckCallback(bool processed) {
108 }
109
110 class MockClientSideDetectionService : public ClientSideDetectionService {
111  public:
112   MockClientSideDetectionService() : ClientSideDetectionService(NULL) {}
113   virtual ~MockClientSideDetectionService() {}
114
115   MOCK_METHOD2(SendClientReportPhishingRequest,
116                void(ClientPhishingRequest*,
117                     const ClientReportPhishingRequestCallback&));
118   MOCK_METHOD2(SendClientReportMalwareRequest,
119                void(ClientMalwareRequest*,
120                     const ClientReportMalwareRequestCallback&));
121   MOCK_CONST_METHOD1(IsPrivateIPAddress, bool(const std::string&));
122   MOCK_METHOD2(GetValidCachedResult, bool(const GURL&, bool*));
123   MOCK_METHOD1(IsInCache, bool(const GURL&));
124   MOCK_METHOD0(OverPhishingReportLimit, bool());
125   MOCK_METHOD0(OverMalwareReportLimit, bool());
126
127  private:
128   DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
129 };
130
131 class MockSafeBrowsingUIManager : public SafeBrowsingUIManager {
132  public:
133   explicit MockSafeBrowsingUIManager(SafeBrowsingService* service)
134       : SafeBrowsingUIManager(service) { }
135
136   MOCK_METHOD1(DisplayBlockingPage, void(const UnsafeResource& resource));
137
138   // Helper function which calls OnBlockingPageComplete for this client
139   // object.
140   void InvokeOnBlockingPageComplete(const UrlCheckCallback& callback) {
141     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
142     // Note: this will delete the client object in the case of the CsdClient
143     // implementation.
144     if (!callback.is_null())
145       callback.Run(false);
146   }
147
148  protected:
149   virtual ~MockSafeBrowsingUIManager() { }
150
151  private:
152   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager);
153 };
154
155 class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
156  public:
157   explicit MockSafeBrowsingDatabaseManager(SafeBrowsingService* service)
158       : SafeBrowsingDatabaseManager(service) { }
159
160   MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&));
161   MOCK_METHOD1(MatchMalwareIP, bool(const std::string& ip_address));
162   MOCK_METHOD0(IsMalwareKillSwitchOn, bool());
163
164  protected:
165   virtual ~MockSafeBrowsingDatabaseManager() {}
166
167  private:
168   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
169 };
170
171 class MockTestingProfile : public TestingProfile {
172  public:
173   MockTestingProfile() {}
174   virtual ~MockTestingProfile() {}
175
176   MOCK_CONST_METHOD0(IsOffTheRecord, bool());
177 };
178
179 class MockBrowserFeatureExtractor : public BrowserFeatureExtractor {
180  public:
181   explicit MockBrowserFeatureExtractor(
182       WebContents* tab,
183       ClientSideDetectionHost* host)
184       : BrowserFeatureExtractor(tab, host) {}
185   virtual ~MockBrowserFeatureExtractor() {}
186
187   MOCK_METHOD3(ExtractFeatures,
188                void(const BrowseInfo*,
189                     ClientPhishingRequest*,
190                     const BrowserFeatureExtractor::DoneCallback&));
191
192   MOCK_METHOD3(ExtractMalwareFeatures,
193                void(BrowseInfo*,
194                     ClientMalwareRequest*,
195                     const BrowserFeatureExtractor::MalwareDoneCallback&));
196 };
197
198 }  // namespace
199
200 class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
201  public:
202   typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource;
203
204   virtual void SetUp() {
205     ChromeRenderViewHostTestHarness::SetUp();
206
207     // Inject service classes.
208     csd_service_.reset(new StrictMock<MockClientSideDetectionService>());
209     // Only used for initializing mock objects.
210     SafeBrowsingService* sb_service =
211         SafeBrowsingService::CreateSafeBrowsingService();
212     database_manager_ =
213         new StrictMock<MockSafeBrowsingDatabaseManager>(sb_service);
214     ui_manager_ = new StrictMock<MockSafeBrowsingUIManager>(sb_service);
215     csd_host_.reset(safe_browsing::ClientSideDetectionHost::Create(
216         web_contents()));
217     csd_host_->set_client_side_detection_service(csd_service_.get());
218     csd_host_->set_safe_browsing_managers(ui_manager_.get(),
219                                           database_manager_.get());
220     // We need to create this here since we don't call DidStopLanding in
221     // this test.
222     csd_host_->browse_info_.reset(new BrowseInfo);
223   }
224
225   virtual void TearDown() {
226     // Delete the host object on the UI thread and release the
227     // SafeBrowsingService.
228     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE,
229                               csd_host_.release());
230     database_manager_ = NULL;
231     ui_manager_ = NULL;
232     base::RunLoop().RunUntilIdle();
233     ChromeRenderViewHostTestHarness::TearDown();
234   }
235
236   virtual content::BrowserContext* CreateBrowserContext() OVERRIDE {
237     // Set custom profile object so that we can mock calls to IsOffTheRecord.
238     // This needs to happen before we call the parent SetUp() function.  We use
239     // a nice mock because other parts of the code are calling IsOffTheRecord.
240     mock_profile_ = new NiceMock<MockTestingProfile>();
241     return mock_profile_;
242   }
243
244   void OnPhishingDetectionDone(const std::string& verdict_str) {
245     csd_host_->OnPhishingDetectionDone(verdict_str);
246   }
247
248   void DidStopLoading() {
249     csd_host_->DidStopLoading(pending_rvh());
250   }
251
252   void UpdateIPUrlMap(const std::string& ip, const std::string& host) {
253     csd_host_->UpdateIPUrlMap(ip, host, "", "", content::RESOURCE_TYPE_OBJECT);
254   }
255
256   BrowseInfo* GetBrowseInfo() {
257     return csd_host_->browse_info_.get();
258   }
259
260   void ExpectPreClassificationChecks(const GURL& url,
261                                      const bool* is_private,
262                                      const bool* is_incognito,
263                                      const bool* match_csd_whitelist,
264                                      const bool* malware_killswitch,
265                                      const bool* get_valid_cached_result,
266                                      const bool* is_in_cache,
267                                      const bool* over_phishing_report_limit,
268                                      const bool* over_malware_report_limit) {
269     if (is_private) {
270       EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
271           .WillOnce(Return(*is_private));
272     }
273     if (is_incognito) {
274       EXPECT_CALL(*mock_profile_, IsOffTheRecord())
275           .WillRepeatedly(Return(*is_incognito));
276     }
277     if (match_csd_whitelist) {
278       EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(url))
279           .WillOnce(Return(*match_csd_whitelist));
280     }
281     if (malware_killswitch) {
282       EXPECT_CALL(*database_manager_.get(), IsMalwareKillSwitchOn())
283           .WillRepeatedly(Return(*malware_killswitch));
284     }
285     if (get_valid_cached_result) {
286       EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull()))
287           .WillOnce(DoAll(SetArgumentPointee<1>(true),
288                           Return(*get_valid_cached_result)));
289     }
290     if (is_in_cache) {
291       EXPECT_CALL(*csd_service_, IsInCache(url)).WillOnce(Return(*is_in_cache));
292     }
293     if (over_phishing_report_limit) {
294       EXPECT_CALL(*csd_service_, OverPhishingReportLimit())
295           .WillOnce(Return(*over_phishing_report_limit));
296     }
297     if (over_malware_report_limit) {
298       EXPECT_CALL(*csd_service_, OverMalwareReportLimit())
299           .WillOnce(Return(*over_malware_report_limit));
300     }
301   }
302
303   void WaitAndCheckPreClassificationChecks() {
304     // Wait for CheckCsdWhitelist and CheckCache() to be called if at all.
305     base::RunLoop().RunUntilIdle();
306     EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
307     EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
308     EXPECT_TRUE(Mock::VerifyAndClear(database_manager_.get()));
309     EXPECT_TRUE(Mock::VerifyAndClear(mock_profile_));
310   }
311
312   void SetFeatureExtractor(BrowserFeatureExtractor* extractor) {
313     csd_host_->feature_extractor_.reset(extractor);
314   }
315
316   void SetRedirectChain(const std::vector<GURL>& redirect_chain) {
317     csd_host_->browse_info_->url_redirects = redirect_chain;
318   }
319
320   void SetReferrer(const GURL& referrer) {
321     csd_host_->browse_info_->referrer = referrer;
322   }
323
324   void ExpectShouldClassifyForMalwareResult(bool should_classify) {
325     EXPECT_EQ(should_classify, csd_host_->should_classify_for_malware_);
326   }
327
328   void ExpectStartPhishingDetection(const GURL* url) {
329     const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
330         SafeBrowsingMsg_StartPhishingDetection::ID);
331     if (url) {
332       ASSERT_TRUE(msg);
333       Tuple1<GURL> actual_url;
334       SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
335       EXPECT_EQ(*url, actual_url.a);
336       EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
337       process()->sink().ClearMessages();
338     } else {
339       ASSERT_FALSE(msg);
340     }
341   }
342
343   void TestUnsafeResourceCopied(const UnsafeResource& resource) {
344     ASSERT_TRUE(csd_host_->unsafe_resource_.get());
345     // Test that the resource from OnSafeBrowsingHit notification was copied
346     // into the CSDH.
347     EXPECT_EQ(resource.url, csd_host_->unsafe_resource_->url);
348     EXPECT_EQ(resource.original_url, csd_host_->unsafe_resource_->original_url);
349     EXPECT_EQ(resource.is_subresource,
350               csd_host_->unsafe_resource_->is_subresource);
351     EXPECT_EQ(resource.threat_type, csd_host_->unsafe_resource_->threat_type);
352     EXPECT_TRUE(csd_host_->unsafe_resource_->callback.is_null());
353     EXPECT_EQ(resource.render_process_host_id,
354               csd_host_->unsafe_resource_->render_process_host_id);
355     EXPECT_EQ(resource.render_view_id,
356               csd_host_->unsafe_resource_->render_view_id);
357   }
358
359   void SetUnsafeSubResourceForCurrent() {
360     UnsafeResource resource;
361     resource.url = GURL("http://www.malware.com/");
362     resource.original_url = web_contents()->GetURL();
363     resource.is_subresource = true;
364     resource.threat_type = SB_THREAT_TYPE_URL_MALWARE;
365     resource.callback = base::Bind(&EmptyUrlCheckCallback);
366     resource.render_process_host_id = web_contents()->GetRenderProcessHost()->
367         GetID();
368     resource.render_view_id =
369         web_contents()->GetRenderViewHost()->GetRoutingID();
370     ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch());
371     csd_host_->OnSafeBrowsingMatch(resource);
372     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
373     csd_host_->OnSafeBrowsingHit(resource);
374     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
375     resource.callback.Reset();
376     ASSERT_TRUE(csd_host_->DidShowSBInterstitial());
377     TestUnsafeResourceCopied(resource);
378   }
379
380   void NavigateWithSBHitAndCommit(const GURL& url) {
381     // Create a pending navigation.
382     controller().LoadURL(
383         url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
384
385     ASSERT_TRUE(pending_rvh());
386     if (web_contents()->GetRenderViewHost()->GetProcess()->GetID() ==
387         pending_rvh()->GetProcess()->GetID()) {
388       EXPECT_NE(web_contents()->GetRenderViewHost()->GetRoutingID(),
389                 pending_rvh()->GetRoutingID());
390     }
391
392     // Simulate a safebrowsing hit before navigation completes.
393     UnsafeResource resource;
394     resource.url = url;
395     resource.original_url = url;
396     resource.is_subresource = false;
397     resource.threat_type = SB_THREAT_TYPE_URL_MALWARE;
398     resource.callback = base::Bind(&EmptyUrlCheckCallback);
399     resource.render_process_host_id = pending_rvh()->GetProcess()->GetID();
400     resource.render_view_id = pending_rvh()->GetRoutingID();
401     csd_host_->OnSafeBrowsingMatch(resource);
402     csd_host_->OnSafeBrowsingHit(resource);
403     resource.callback.Reset();
404
405     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
406
407     // LoadURL created a navigation entry, now simulate the RenderView sending
408     // a notification that it actually navigated.
409     content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
410
411     ASSERT_TRUE(csd_host_->DidPageReceiveSafeBrowsingMatch());
412     ASSERT_TRUE(csd_host_->DidShowSBInterstitial());
413     TestUnsafeResourceCopied(resource);
414   }
415
416   void NavigateWithoutSBHitAndCommit(const GURL& safe_url) {
417     controller().LoadURL(
418         safe_url, content::Referrer(), content::PAGE_TRANSITION_LINK,
419         std::string());
420
421     ASSERT_TRUE(pending_rvh());
422     if (web_contents()->GetRenderViewHost()->GetProcess()->GetID() ==
423         pending_rvh()->GetProcess()->GetID()) {
424       EXPECT_NE(web_contents()->GetRenderViewHost()->GetRoutingID(),
425                 pending_rvh()->GetRoutingID());
426     }
427     ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch());
428     ASSERT_FALSE(csd_host_->DidShowSBInterstitial());
429
430     content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
431     ASSERT_FALSE(csd_host_->DidPageReceiveSafeBrowsingMatch());
432     ASSERT_FALSE(csd_host_->DidShowSBInterstitial());
433   }
434
435   void CheckIPUrlEqual(const std::vector<IPUrlInfo>& expect,
436                        const std::vector<IPUrlInfo>& result) {
437     ASSERT_EQ(expect.size(), result.size());
438
439     for (unsigned int i = 0; i < expect.size(); ++i) {
440       EXPECT_EQ(expect[i].url, result[i].url);
441       EXPECT_EQ(expect[i].method, result[i].method);
442       EXPECT_EQ(expect[i].referrer, result[i].referrer);
443       EXPECT_EQ(expect[i].resource_type, result[i].resource_type);
444     }
445   }
446
447  protected:
448   scoped_ptr<ClientSideDetectionHost> csd_host_;
449   scoped_ptr<StrictMock<MockClientSideDetectionService> > csd_service_;
450   scoped_refptr<StrictMock<MockSafeBrowsingUIManager> > ui_manager_;
451   scoped_refptr<StrictMock<MockSafeBrowsingDatabaseManager> > database_manager_;
452   MockTestingProfile* mock_profile_;  // We don't own this object
453 };
454
455 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneInvalidVerdict) {
456   // Case 0: renderer sends an invalid verdict string that we're unable to
457   // parse.
458   MockBrowserFeatureExtractor* mock_extractor =
459       new StrictMock<MockBrowserFeatureExtractor>(
460           web_contents(),
461           csd_host_.get());
462   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
463   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
464   OnPhishingDetectionDone("Invalid Protocol Buffer");
465   EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
466 }
467
468 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneNotPhishing) {
469   // Case 1: client thinks the page is phishing.  The server does not agree.
470   // No interstitial is shown.
471   MockBrowserFeatureExtractor* mock_extractor =
472       new StrictMock<MockBrowserFeatureExtractor>(
473           web_contents(),
474           csd_host_.get());
475   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
476
477   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
478   ClientPhishingRequest verdict;
479   verdict.set_url("http://phishingurl.com/");
480   verdict.set_client_score(1.0f);
481   verdict.set_is_phishing(true);
482
483   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
484       .WillOnce(InvokeDoneCallback(&verdict));
485   EXPECT_CALL(*csd_service_,
486               SendClientReportPhishingRequest(
487                   Pointee(PartiallyEqualVerdict(verdict)), _))
488       .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
489   OnPhishingDetectionDone(verdict.SerializeAsString());
490   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
491   ASSERT_FALSE(cb.is_null());
492
493   // Make sure DisplayBlockingPage is not going to be called.
494   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
495   cb.Run(GURL(verdict.url()), false);
496   base::RunLoop().RunUntilIdle();
497   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
498 }
499
500 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneDisabled) {
501   // Case 2: client thinks the page is phishing and so does the server but
502   // showing the interstitial is disabled => no interstitial is shown.
503   MockBrowserFeatureExtractor* mock_extractor =
504       new StrictMock<MockBrowserFeatureExtractor>(
505           web_contents(),
506           csd_host_.get());
507   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
508
509   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
510   ClientPhishingRequest verdict;
511   verdict.set_url("http://phishingurl.com/");
512   verdict.set_client_score(1.0f);
513   verdict.set_is_phishing(true);
514
515   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
516       .WillOnce(InvokeDoneCallback(&verdict));
517   EXPECT_CALL(*csd_service_,
518               SendClientReportPhishingRequest(
519                   Pointee(PartiallyEqualVerdict(verdict)), _))
520       .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
521   OnPhishingDetectionDone(verdict.SerializeAsString());
522   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
523   ASSERT_FALSE(cb.is_null());
524
525   // Make sure DisplayBlockingPage is not going to be called.
526   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
527   cb.Run(GURL(verdict.url()), false);
528   base::RunLoop().RunUntilIdle();
529   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
530 }
531
532 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneShowInterstitial) {
533   // Case 3: client thinks the page is phishing and so does the server.
534   // We show an interstitial.
535   MockBrowserFeatureExtractor* mock_extractor =
536       new StrictMock<MockBrowserFeatureExtractor>(
537           web_contents(),
538           csd_host_.get());
539   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
540
541   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
542   GURL phishing_url("http://phishingurl.com/");
543   ClientPhishingRequest verdict;
544   verdict.set_url(phishing_url.spec());
545   verdict.set_client_score(1.0f);
546   verdict.set_is_phishing(true);
547
548   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
549       .WillOnce(InvokeDoneCallback(&verdict));
550   EXPECT_CALL(*csd_service_,
551               SendClientReportPhishingRequest(
552                   Pointee(PartiallyEqualVerdict(verdict)), _))
553       .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
554   OnPhishingDetectionDone(verdict.SerializeAsString());
555   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
556   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
557   ASSERT_FALSE(cb.is_null());
558
559   UnsafeResource resource;
560   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
561       .WillOnce(SaveArg<0>(&resource));
562   cb.Run(phishing_url, true);
563
564   base::RunLoop().RunUntilIdle();
565   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
566   EXPECT_EQ(phishing_url, resource.url);
567   EXPECT_EQ(phishing_url, resource.original_url);
568   EXPECT_FALSE(resource.is_subresource);
569   EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, resource.threat_type);
570   EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
571             resource.render_process_host_id);
572   EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
573             resource.render_view_id);
574
575   // Make sure the client object will be deleted.
576   BrowserThread::PostTask(
577       BrowserThread::IO,
578       FROM_HERE,
579       base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
580                  ui_manager_, resource.callback));
581 }
582
583 TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneMultiplePings) {
584   // Case 4 & 5: client thinks a page is phishing then navigates to
585   // another page which is also considered phishing by the client
586   // before the server responds with a verdict.  After a while the
587   // server responds for both requests with a phishing verdict.  Only
588   // a single interstitial is shown for the second URL.
589   MockBrowserFeatureExtractor* mock_extractor =
590       new StrictMock<MockBrowserFeatureExtractor>(
591           web_contents(),
592           csd_host_.get());
593   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
594
595   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
596   GURL phishing_url("http://phishingurl.com/");
597   ClientPhishingRequest verdict;
598   verdict.set_url(phishing_url.spec());
599   verdict.set_client_score(1.0f);
600   verdict.set_is_phishing(true);
601
602   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
603       .WillOnce(InvokeDoneCallback(&verdict));
604   EXPECT_CALL(*csd_service_,
605               SendClientReportPhishingRequest(
606                   Pointee(PartiallyEqualVerdict(verdict)), _))
607       .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
608   OnPhishingDetectionDone(verdict.SerializeAsString());
609   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
610   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
611   ASSERT_FALSE(cb.is_null());
612
613   // Set this back to a normal browser feature extractor since we're using
614   // NavigateAndCommit() and it's easier to use the real thing than setting up
615   // mock expectations.
616   SetFeatureExtractor(new BrowserFeatureExtractor(web_contents(),
617                                                   csd_host_.get()));
618   GURL other_phishing_url("http://other_phishing_url.com/bla");
619   ExpectPreClassificationChecks(other_phishing_url, &kFalse, &kFalse, &kFalse,
620                                 &kFalse, &kFalse, &kFalse, &kFalse, &kFalse);
621   // We navigate away.  The callback cb should be revoked.
622   NavigateAndCommit(other_phishing_url);
623   // Wait for the pre-classification checks to finish for other_phishing_url.
624   WaitAndCheckPreClassificationChecks();
625
626   ClientSideDetectionService::ClientReportPhishingRequestCallback cb_other;
627   verdict.set_url(other_phishing_url.spec());
628   verdict.set_client_score(0.8f);
629   EXPECT_CALL(*csd_service_,
630               SendClientReportPhishingRequest(
631                   Pointee(PartiallyEqualVerdict(verdict)), _))
632       .WillOnce(DoAll(DeleteArg<0>(),
633                       SaveArg<1>(&cb_other),
634                       QuitUIMessageLoop()));
635   std::vector<GURL> redirect_chain;
636   redirect_chain.push_back(other_phishing_url);
637   SetRedirectChain(redirect_chain);
638   OnPhishingDetectionDone(verdict.SerializeAsString());
639   base::MessageLoop::current()->Run();
640   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
641   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
642   ASSERT_FALSE(cb_other.is_null());
643
644   // We expect that the interstitial is shown for the second phishing URL and
645   // not for the first phishing URL.
646   UnsafeResource resource;
647   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
648       .WillOnce(SaveArg<0>(&resource));
649
650   cb.Run(phishing_url, true);  // Should have no effect.
651   cb_other.Run(other_phishing_url, true);  // Should show interstitial.
652
653   base::RunLoop().RunUntilIdle();
654   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
655   EXPECT_EQ(other_phishing_url, resource.url);
656   EXPECT_EQ(other_phishing_url, resource.original_url);
657   EXPECT_FALSE(resource.is_subresource);
658   EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, resource.threat_type);
659   EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
660             resource.render_process_host_id);
661   EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
662             resource.render_view_id);
663
664   // Make sure the client object will be deleted.
665   BrowserThread::PostTask(
666       BrowserThread::IO,
667       FROM_HERE,
668       base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
669                  ui_manager_, resource.callback));
670 }
671
672 TEST_F(ClientSideDetectionHostTest,
673        OnPhishingDetectionDoneVerdictNotPhishing) {
674   // Case 6: renderer sends a verdict string that isn't phishing.
675   MockBrowserFeatureExtractor* mock_extractor =
676       new StrictMock<MockBrowserFeatureExtractor>(
677           web_contents(),
678           csd_host_.get());
679   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
680
681   ClientPhishingRequest verdict;
682   verdict.set_url("http://not-phishing.com/");
683   verdict.set_client_score(0.1f);
684   verdict.set_is_phishing(false);
685
686   EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
687   OnPhishingDetectionDone(verdict.SerializeAsString());
688   EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
689 }
690
691 TEST_F(ClientSideDetectionHostTest,
692        OnPhishingDetectionDoneVerdictNotPhishingButSBMatchSubResource) {
693   // Case 7: renderer sends a verdict string that isn't phishing but the URL
694   // of a subresource was on the regular phishing or malware lists.
695   GURL url("http://not-phishing.com/");
696   ClientPhishingRequest verdict;
697   verdict.set_url(url.spec());
698   verdict.set_client_score(0.1f);
699   verdict.set_is_phishing(false);
700
701   // First we have to navigate to the URL to set the unique page ID.
702   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
703                                 &kFalse, &kFalse, &kFalse, &kFalse);
704   NavigateAndCommit(url);
705   WaitAndCheckPreClassificationChecks();
706   SetUnsafeSubResourceForCurrent();
707
708   EXPECT_CALL(*csd_service_,
709               SendClientReportPhishingRequest(
710                   Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull()))
711       .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop()));
712   std::vector<GURL> redirect_chain;
713   redirect_chain.push_back(url);
714   SetRedirectChain(redirect_chain);
715   OnPhishingDetectionDone(verdict.SerializeAsString());
716   base::MessageLoop::current()->Run();
717   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
718 }
719
720 TEST_F(ClientSideDetectionHostTest,
721        OnPhishingDetectionDoneVerdictNotPhishingButSBMatchOnNewRVH) {
722   // When navigating to a different host (thus creating a pending RVH) which
723   // matches regular malware list, and after navigation the renderer sends a
724   // verdict string that isn't phishing, we should still send the report.
725
726   // Do an initial navigation to a safe host.
727   GURL start_url("http://safe.example.com/");
728   ExpectPreClassificationChecks(
729       start_url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse,
730       &kFalse);
731   NavigateAndCommit(start_url);
732   WaitAndCheckPreClassificationChecks();
733
734   // Now navigate to a different host which will have a malware hit before the
735   // navigation commits.
736   GURL url("http://malware-but-not-phishing.com/");
737   ClientPhishingRequest verdict;
738   verdict.set_url(url.spec());
739   verdict.set_client_score(0.1f);
740   verdict.set_is_phishing(false);
741
742   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
743                                 &kFalse, &kFalse, &kFalse, &kFalse);
744   NavigateWithSBHitAndCommit(url);
745   WaitAndCheckPreClassificationChecks();
746
747   EXPECT_CALL(*csd_service_,
748               SendClientReportPhishingRequest(
749                   Pointee(PartiallyEqualVerdict(verdict)), CallbackIsNull()))
750       .WillOnce(DoAll(DeleteArg<0>(), QuitUIMessageLoop()));
751   std::vector<GURL> redirect_chain;
752   redirect_chain.push_back(url);
753   SetRedirectChain(redirect_chain);
754   OnPhishingDetectionDone(verdict.SerializeAsString());
755   base::MessageLoop::current()->Run();
756   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
757
758   ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse,
759                                 &kFalse, &kFalse, &kFalse, &kFalse);
760   NavigateWithoutSBHitAndCommit(start_url);
761   WaitAndCheckPreClassificationChecks();
762 }
763
764 TEST_F(ClientSideDetectionHostTest,
765        DidStopLoadingShowMalwareInterstitial) {
766   // Case 9: client thinks the page match malware IP and so does the server.
767   // We show an sub-resource malware interstitial.
768   MockBrowserFeatureExtractor* mock_extractor =
769       new StrictMock<MockBrowserFeatureExtractor>(
770           web_contents(),
771           csd_host_.get());
772   SetFeatureExtractor(mock_extractor);  // The host class takes ownership.
773
774   GURL malware_landing_url("http://malware.com/");
775   GURL malware_ip_url("http://badip.com");
776   ClientMalwareRequest malware_verdict;
777   malware_verdict.set_url("http://malware.com/");
778   ClientMalwareRequest::UrlInfo* badipurl =
779       malware_verdict.add_bad_ip_url_info();
780   badipurl->set_ip("1.2.3.4");
781   badipurl->set_url("http://badip.com");
782
783   ExpectPreClassificationChecks(GURL(malware_verdict.url()), &kFalse, &kFalse,
784                                 &kFalse, &kFalse, &kFalse, &kFalse, &kFalse,
785                                 &kFalse);
786   NavigateAndCommit(GURL(malware_verdict.url()));
787   WaitAndCheckPreClassificationChecks();
788
789   ClientSideDetectionService::ClientReportMalwareRequestCallback cb;
790   EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
791       .WillOnce(InvokeMalwareCallback(&malware_verdict));
792   EXPECT_CALL(*csd_service_,
793               SendClientReportMalwareRequest(
794                   Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
795       .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
796   DidStopLoading();
797   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
798   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
799   ASSERT_FALSE(cb.is_null());
800
801   UnsafeResource resource;
802   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
803       .WillOnce(SaveArg<0>(&resource));
804   cb.Run(malware_landing_url, malware_ip_url, true);
805
806   base::RunLoop().RunUntilIdle();
807   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
808   EXPECT_EQ(malware_ip_url, resource.url);
809   EXPECT_EQ(malware_landing_url, resource.original_url);
810   EXPECT_TRUE(resource.is_subresource);
811   EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, resource.threat_type);
812   EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
813             resource.render_process_host_id);
814   EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
815             resource.render_view_id);
816
817   // Make sure the client object will be deleted.
818   BrowserThread::PostTask(
819       BrowserThread::IO,
820       FROM_HERE,
821       base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
822                  ui_manager_, resource.callback));
823 }
824
825 TEST_F(ClientSideDetectionHostTest, UpdateIPUrlMap) {
826   BrowseInfo* browse_info = GetBrowseInfo();
827
828   // Empty IP or host are skipped
829   UpdateIPUrlMap("250.10.10.10", std::string());
830   ASSERT_EQ(0U, browse_info->ips.size());
831   UpdateIPUrlMap(std::string(), "http://google.com/a");
832   ASSERT_EQ(0U, browse_info->ips.size());
833   UpdateIPUrlMap(std::string(), std::string());
834   ASSERT_EQ(0U, browse_info->ips.size());
835
836   std::vector<IPUrlInfo> expected_urls;
837   for (int i = 0; i < 20; i++) {
838     std::string url = base::StringPrintf("http://%d.com/", i);
839     expected_urls.push_back(
840         IPUrlInfo(url, "", "", content::RESOURCE_TYPE_OBJECT));
841     UpdateIPUrlMap("250.10.10.10", url);
842   }
843   ASSERT_EQ(1U, browse_info->ips.size());
844   ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size());
845   CheckIPUrlEqual(expected_urls,
846                   browse_info->ips["250.10.10.10"]);
847
848   // Add more urls for this ip, it exceeds max limit and won't be added
849   UpdateIPUrlMap("250.10.10.10", "http://21.com/");
850   ASSERT_EQ(1U, browse_info->ips.size());
851   ASSERT_EQ(20U, browse_info->ips["250.10.10.10"].size());
852   CheckIPUrlEqual(expected_urls,
853                   browse_info->ips["250.10.10.10"]);
854
855   // Add 199 more IPs
856   for (int i = 0; i < 199; i++) {
857     std::string ip = base::StringPrintf("%d.%d.%d.256", i, i, i);
858     expected_urls.clear();
859     expected_urls.push_back(
860         IPUrlInfo("test.com/", "", "", content::RESOURCE_TYPE_OBJECT));
861     UpdateIPUrlMap(ip, "test.com/");
862     ASSERT_EQ(1U, browse_info->ips[ip].size());
863     CheckIPUrlEqual(expected_urls,
864                     browse_info->ips[ip]);
865   }
866   ASSERT_EQ(200U, browse_info->ips.size());
867
868   // Exceeding max ip limit 200, these won't be added
869   UpdateIPUrlMap("250.250.250.250", "goo.com/");
870   UpdateIPUrlMap("250.250.250.250", "bar.com/");
871   UpdateIPUrlMap("250.250.0.250", "foo.com/");
872   ASSERT_EQ(200U, browse_info->ips.size());
873
874   // Add url to existing IPs succeed
875   UpdateIPUrlMap("100.100.100.256", "more.com/");
876   ASSERT_EQ(2U, browse_info->ips["100.100.100.256"].size());
877   expected_urls.clear();
878   expected_urls.push_back(
879       IPUrlInfo("test.com/", "", "", content::RESOURCE_TYPE_OBJECT));
880   expected_urls.push_back(
881       IPUrlInfo("more.com/", "", "", content::RESOURCE_TYPE_OBJECT));
882   CheckIPUrlEqual(expected_urls,
883                   browse_info->ips["100.100.100.256"]);
884 }
885
886 TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
887   // Test that canceling pending should classify requests works as expected.
888
889   GURL first_url("http://first.phishy.url.com");
890   GURL second_url("http://second.url.com/");
891   // The first few checks are done synchronously so check that they have been
892   // done for the first URL, while the second URL has all the checks done.  We
893   // need to manually set up the IsPrivateIPAddress mock since if the same mock
894   // expectation is specified twice, gmock will only use the last instance of
895   // it, meaning the first will never be matched.
896   EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
897       .WillOnce(Return(false))
898       .WillOnce(Return(false));
899   ExpectPreClassificationChecks(first_url, NULL, &kFalse, &kFalse, &kFalse,
900                                 NULL, NULL, NULL, NULL);
901   ExpectPreClassificationChecks(second_url, NULL, &kFalse, &kFalse, &kFalse,
902                                 &kFalse, &kFalse, &kFalse, &kFalse);
903
904   NavigateAndCommit(first_url);
905   // Don't flush the message loop, as we want to navigate to a different
906   // url before the final pre-classification checks are run.
907   NavigateAndCommit(second_url);
908   WaitAndCheckPreClassificationChecks();
909 }
910
911 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckPass) {
912   // Navigate the tab to a page.  We should see a StartPhishingDetection IPC.
913   GURL url("http://host.com/");
914   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
915                                 &kFalse, &kFalse, &kFalse, &kFalse);
916   NavigateAndCommit(url);
917   WaitAndCheckPreClassificationChecks();
918
919   ExpectStartPhishingDetection(&url);
920   ExpectShouldClassifyForMalwareResult(true);
921 }
922
923 TEST_F(ClientSideDetectionHostTest,
924        TestPreClassificationCheckInPageNavigation) {
925   GURL url("http://host.com/");
926   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
927                                 &kFalse, &kFalse, &kFalse, &kFalse);
928   NavigateAndCommit(url);
929   WaitAndCheckPreClassificationChecks();
930
931   ExpectStartPhishingDetection(&url);
932   ExpectShouldClassifyForMalwareResult(true);
933
934   // Now try an in-page navigation.  This should not trigger an IPC.
935   EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0);
936   GURL inpage("http://host.com/#foo");
937   ExpectPreClassificationChecks(inpage, NULL, NULL, NULL, NULL, NULL, NULL,
938                                 NULL, NULL);
939   NavigateAndCommit(inpage);
940   WaitAndCheckPreClassificationChecks();
941
942   ExpectStartPhishingDetection(NULL);
943   ExpectShouldClassifyForMalwareResult(true);
944 }
945
946 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckXHTML) {
947   // Check that XHTML is supported, in addition to the default HTML type.
948   GURL url("http://host.com/xhtml");
949   rvh_tester()->SetContentsMimeType("application/xhtml+xml");
950   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
951                                 &kFalse, &kFalse, &kFalse, &kFalse);
952   NavigateAndCommit(url);
953   WaitAndCheckPreClassificationChecks();
954
955   ExpectStartPhishingDetection(&url);
956   ExpectShouldClassifyForMalwareResult(true);
957 }
958
959 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckTwoNavigations) {
960   // Navigate to two hosts, which should cause two IPCs.
961   GURL url1("http://host1.com/");
962   ExpectPreClassificationChecks(url1, &kFalse, &kFalse, &kFalse, &kFalse,
963                                 &kFalse, &kFalse, &kFalse, &kFalse);
964   NavigateAndCommit(url1);
965   WaitAndCheckPreClassificationChecks();
966
967   ExpectStartPhishingDetection(&url1);
968   ExpectShouldClassifyForMalwareResult(true);
969
970   GURL url2("http://host2.com/");
971   ExpectPreClassificationChecks(url2, &kFalse, &kFalse, &kFalse, &kFalse,
972                                 &kFalse, &kFalse, &kFalse, &kFalse);
973   NavigateAndCommit(url2);
974   WaitAndCheckPreClassificationChecks();
975
976   ExpectStartPhishingDetection(&url2);
977   ExpectShouldClassifyForMalwareResult(true);
978 }
979
980 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckMimeType) {
981   // If the mime type is not one that we support, no IPC should be triggered
982   // but all pre-classification checks should run because we might classify
983   // other mime types for malware.
984   // Note: for this test to work correctly, the new URL must be on the
985   // same domain as the previous URL, otherwise it will create a new
986   // RenderViewHost that won't have the mime type set.
987   GURL url("http://host2.com/image.jpg");
988   rvh_tester()->SetContentsMimeType("image/jpeg");
989   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
990                                 &kFalse, &kFalse, &kFalse, &kFalse);
991   NavigateAndCommit(url);
992   WaitAndCheckPreClassificationChecks();
993
994   ExpectStartPhishingDetection(NULL);
995   ExpectShouldClassifyForMalwareResult(true);
996 }
997
998 TEST_F(ClientSideDetectionHostTest,
999        TestPreClassificationCheckPrivateIpAddress) {
1000   // If IsPrivateIPAddress returns true, no IPC should be triggered.
1001   GURL url("http://host3.com/");
1002   ExpectPreClassificationChecks(url, &kTrue, &kFalse, NULL, NULL, NULL, NULL,
1003                                 NULL, NULL);
1004   NavigateAndCommit(url);
1005   WaitAndCheckPreClassificationChecks();
1006   const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
1007       SafeBrowsingMsg_StartPhishingDetection::ID);
1008   ASSERT_FALSE(msg);
1009   ExpectShouldClassifyForMalwareResult(false);
1010 }
1011
1012 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckIncognito) {
1013   // If the tab is incognito there should be no IPC.  Also, we shouldn't
1014   // even check the csd-whitelist.
1015   GURL url("http://host4.com/");
1016   ExpectPreClassificationChecks(url, &kFalse, &kTrue, NULL, NULL, NULL, NULL,
1017                                 NULL, NULL);
1018   NavigateAndCommit(url);
1019   WaitAndCheckPreClassificationChecks();
1020
1021   ExpectStartPhishingDetection(NULL);
1022   ExpectShouldClassifyForMalwareResult(false);
1023 }
1024
1025 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckCsdWhitelist) {
1026   // If the URL is on the csd whitelist no phishing IPC should be sent
1027   // but we should classify the URL for malware.
1028   GURL url("http://host5.com/");
1029   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kFalse, &kFalse,
1030                                 &kFalse, &kFalse, &kFalse);
1031   NavigateAndCommit(url);
1032   WaitAndCheckPreClassificationChecks();
1033
1034   ExpectStartPhishingDetection(NULL);
1035   ExpectShouldClassifyForMalwareResult(true);
1036 }
1037
1038 TEST_F(ClientSideDetectionHostTest,
1039        TestPreClassificationCheckMalwareKillSwitch) {
1040   // If the malware killswitch is on we shouldn't classify the page for malware.
1041   GURL url("http://host5.com/kill-switch");
1042   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, &kFalse,
1043                                 &kFalse, &kFalse, &kFalse);
1044   NavigateAndCommit(url);
1045   WaitAndCheckPreClassificationChecks();
1046
1047   ExpectStartPhishingDetection(&url);
1048   ExpectShouldClassifyForMalwareResult(false);
1049 }
1050
1051 TEST_F(ClientSideDetectionHostTest,
1052        TestPreClassificationCheckKillswitchAndCsdWhitelist) {
1053   // If both the malware kill-swtich is on and the URL is on the csd whitelist,
1054   // we will leave pre-classification checks early.
1055   GURL url("http://host5.com/kill-switch-and-whitelisted");
1056   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kTrue, NULL,
1057                                 NULL, NULL, NULL);
1058   NavigateAndCommit(url);
1059   WaitAndCheckPreClassificationChecks();
1060
1061   ExpectStartPhishingDetection(NULL);
1062   ExpectShouldClassifyForMalwareResult(false);
1063 }
1064
1065 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckInvalidCache) {
1066   // If item is in the cache but it isn't valid, we will classify regardless
1067   // of whether we are over the reporting limit.
1068   GURL url("http://host6.com/");
1069   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
1070                                 &kFalse, &kTrue, NULL, &kFalse);
1071
1072   NavigateAndCommit(url);
1073   WaitAndCheckPreClassificationChecks();
1074
1075   ExpectStartPhishingDetection(&url);
1076   ExpectShouldClassifyForMalwareResult(true);
1077 }
1078
1079 TEST_F(ClientSideDetectionHostTest,
1080        TestPreClassificationCheckOverPhishingReportingLimit) {
1081   // If the url isn't in the cache and we are over the reporting limit, we
1082   // don't do classification.
1083   GURL url("http://host7.com/");
1084   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
1085                                 &kFalse, &kFalse, &kTrue, &kFalse);
1086   NavigateAndCommit(url);
1087   WaitAndCheckPreClassificationChecks();
1088
1089   ExpectStartPhishingDetection(NULL);
1090   ExpectShouldClassifyForMalwareResult(true);
1091 }
1092
1093 TEST_F(ClientSideDetectionHostTest,
1094        TestPreClassificationCheckOverMalwareReportingLimit) {
1095   GURL url("http://host.com/");
1096   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
1097                                 &kFalse, &kFalse, &kFalse, &kTrue);
1098   NavigateAndCommit(url);
1099   WaitAndCheckPreClassificationChecks();
1100
1101   ExpectStartPhishingDetection(&url);
1102   ExpectShouldClassifyForMalwareResult(false);
1103 }
1104
1105 TEST_F(ClientSideDetectionHostTest,
1106        TestPreClassificationCheckOverBothReportingLimits) {
1107   GURL url("http://host.com/");
1108   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
1109                                 &kFalse, &kFalse, &kTrue, &kTrue);
1110   NavigateAndCommit(url);
1111   WaitAndCheckPreClassificationChecks();
1112
1113   ExpectStartPhishingDetection(NULL);
1114   ExpectShouldClassifyForMalwareResult(false);
1115 }
1116
1117 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckHttpsUrl) {
1118   GURL url("https://host.com/");
1119   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
1120                                 &kFalse, &kFalse, &kFalse, &kFalse);
1121   NavigateAndCommit(url);
1122   WaitAndCheckPreClassificationChecks();
1123
1124   ExpectStartPhishingDetection(NULL);
1125   ExpectShouldClassifyForMalwareResult(true);
1126 }
1127
1128 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckValidCached) {
1129   // If result is cached, we will try and display the blocking page directly
1130   // with no start classification message.
1131   GURL url("http://host8.com/");
1132   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kTrue,
1133                                 &kFalse, &kFalse, &kFalse);
1134
1135   UnsafeResource resource;
1136   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
1137       .WillOnce(SaveArg<0>(&resource));
1138
1139   NavigateAndCommit(url);
1140   WaitAndCheckPreClassificationChecks();
1141   EXPECT_EQ(url, resource.url);
1142   EXPECT_EQ(url, resource.original_url);
1143
1144   ExpectStartPhishingDetection(NULL);
1145
1146   // Showing a phishing warning will invalidate all the weak pointers which
1147   // means we will not extract malware features.
1148   ExpectShouldClassifyForMalwareResult(false);
1149 }
1150 }  // namespace safe_browsing