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 "chrome/browser/safe_browsing/client_side_detection_host.h"
6 
7 #include <memory>
8 #include <tuple>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/files/file_path.h"
14 #include "base/macros.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/run_loop.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/test/gmock_move_support.h"
20 #include "base/test/metrics/histogram_tester.h"
21 #include "base/test/simple_test_tick_clock.h"
22 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
23 #include "chrome/browser/safe_browsing/client_side_model_loader.h"
24 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
25 #include "chrome/browser/safe_browsing/ui_manager.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/prefs/scoped_user_pref_update.h"
30 #include "components/safe_browsing/content/common/safe_browsing.mojom-shared.h"
31 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
32 #include "components/safe_browsing/core/db/database_manager.h"
33 #include "components/safe_browsing/core/db/test_database_manager.h"
34 #include "components/safe_browsing/core/features.h"
35 #include "components/safe_browsing/core/proto/csd.pb.h"
36 #include "components/security_interstitials/content/unsafe_resource_util.h"
37 #include "components/security_interstitials/core/unsafe_resource.h"
38 #include "content/public/browser/browser_task_traits.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/navigation_entry.h"
41 #include "content/public/browser/render_frame_host.h"
42 #include "content/public/browser/web_contents.h"
43 #include "content/public/test/mock_render_process_host.h"
44 #include "content/public/test/navigation_simulator.h"
45 #include "content/public/test/test_renderer_host.h"
46 #include "content/public/test/web_contents_tester.h"
47 #include "ipc/ipc_test_sink.h"
48 #include "mojo/public/cpp/bindings/pending_receiver.h"
49 #include "mojo/public/cpp/bindings/receiver_set.h"
50 #include "services/network/public/cpp/shared_url_loader_factory.h"
51 #include "testing/gmock/include/gmock/gmock.h"
52 #include "testing/gtest/include/gtest/gtest.h"
53 #include "url/gurl.h"
54 
55 using content::BrowserThread;
56 using content::RenderFrameHostTester;
57 using content::WebContents;
58 using ::testing::_;
59 using ::testing::DeleteArg;
60 using ::testing::DoAll;
61 using ::testing::Eq;
62 using ::testing::Invoke;
63 using ::testing::IsNull;
64 using ::testing::Mock;
65 using ::testing::NiceMock;
66 using ::testing::NotNull;
67 using ::testing::Pointee;
68 using ::testing::Return;
69 using ::testing::SaveArg;
70 using ::testing::SetArgPointee;
71 using ::testing::StrictMock;
72 
73 namespace {
74 
75 const bool kFalse = false;
76 const bool kTrue = true;
77 
NavigateAndKeepLoading(content::WebContents * web_contents,const GURL & url)78 std::unique_ptr<content::NavigationSimulator> NavigateAndKeepLoading(
79     content::WebContents* web_contents,
80     const GURL& url) {
81   auto navigation =
82       content::NavigationSimulator::CreateBrowserInitiated(url, web_contents);
83   navigation->SetKeepLoading(true);
84   navigation->Commit();
85   return navigation;
86 }
87 
88 }  // namespace
89 
90 namespace safe_browsing {
91 namespace {
92 // This matcher verifies that the client computed verdict
93 // (ClientPhishingRequest) which is passed to SendClientReportPhishingRequest
94 // has the expected fields set.  Note: we can't simply compare the protocol
95 // buffer strings because the BrowserFeatureExtractor might add features to the
96 // verdict object before calling SendClientReportPhishingRequest.
97 MATCHER_P(PartiallyEqualVerdict, other, "") {
98   return (other.url() == arg->url() &&
99           other.client_score() == arg->client_score() &&
100           other.is_phishing() == arg->is_phishing());
101 }
102 
103 // Test that the callback is nullptr when the verdict is not phishing.
104 MATCHER(CallbackIsNull, "") {
105   return arg.is_null();
106 }
107 
108 class MockModelLoader : public ModelLoader {
109  public:
MockModelLoader()110   MockModelLoader() : ModelLoader(base::RepeatingClosure(), nullptr, false) {}
111   ~MockModelLoader() override = default;
112 
113   MOCK_METHOD1(ScheduleFetch, void(int64_t));
114   MOCK_METHOD0(CancelFetcher, void());
115 
116  private:
117   DISALLOW_COPY_AND_ASSIGN(MockModelLoader);
118 };
119 
120 class MockClientSideDetectionService : public ClientSideDetectionService {
121  public:
MockClientSideDetectionService()122   MockClientSideDetectionService() : ClientSideDetectionService(nullptr) {}
~MockClientSideDetectionService()123   ~MockClientSideDetectionService() override {}
124 
125   MOCK_METHOD4(SendClientReportPhishingRequest,
126                void(std::unique_ptr<ClientPhishingRequest>,
127                     bool,
128                     bool,
129                     ClientReportPhishingRequestCallback));
130   MOCK_CONST_METHOD1(IsPrivateIPAddress, bool(const std::string&));
131   MOCK_METHOD2(GetValidCachedResult, bool(const GURL&, bool*));
132   MOCK_METHOD1(IsInCache, bool(const GURL&));
133   MOCK_METHOD0(OverPhishingReportLimit, bool());
134   MOCK_METHOD0(GetModelStr, std::string());
135 
136  private:
137   DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
138 };
139 
140 class MockSafeBrowsingUIManager : public SafeBrowsingUIManager {
141  public:
MockSafeBrowsingUIManager(SafeBrowsingService * service)142   explicit MockSafeBrowsingUIManager(SafeBrowsingService* service)
143       : SafeBrowsingUIManager(service) { }
144 
145   MOCK_METHOD1(DisplayBlockingPage, void(const UnsafeResource& resource));
146 
147   // Helper function which calls OnBlockingPageComplete for this client
148   // object.
InvokeOnBlockingPageComplete(const security_interstitials::UnsafeResource::UrlCheckCallback & callback)149   void InvokeOnBlockingPageComplete(
150     const security_interstitials::UnsafeResource::UrlCheckCallback& callback) {
151     DCHECK_CURRENTLY_ON(BrowserThread::IO);
152     // Note: this will delete the client object in the case of the CsdClient
153     // implementation.
154     if (!callback.is_null())
155       callback.Run(false /*proceed*/, true /*showed_interstitial*/);
156   }
157 
158  protected:
~MockSafeBrowsingUIManager()159   ~MockSafeBrowsingUIManager() override {}
160 
161  private:
162   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager);
163 };
164 
165 class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
166  public:
MockSafeBrowsingDatabaseManager()167   MockSafeBrowsingDatabaseManager() {}
168 
169   MOCK_METHOD2(CheckCsdWhitelistUrl,
170                AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
171 
172  protected:
~MockSafeBrowsingDatabaseManager()173   ~MockSafeBrowsingDatabaseManager() override {}
174 
175  private:
176   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
177 };
178 
179 }  // namespace
180 
181 class FakePhishingDetector : public mojom::PhishingDetector {
182  public:
183   FakePhishingDetector() = default;
184 
185   ~FakePhishingDetector() override = default;
186 
BindReceiver(mojo::ScopedMessagePipeHandle handle)187   void BindReceiver(mojo::ScopedMessagePipeHandle handle) {
188     receivers_.Add(this, mojo::PendingReceiver<mojom::PhishingDetector>(
189                              std::move(handle)));
190   }
191 
192   // mojom::PhishingDetector
SetPhishingModel(const std::string & model)193   void SetPhishingModel(const std::string& model) override { model_ = model; }
194 
195   // mojom::PhishingDetector
StartPhishingDetection(const GURL & url,StartPhishingDetectionCallback callback)196   void StartPhishingDetection(
197       const GURL& url,
198       StartPhishingDetectionCallback callback) override {
199     url_ = url;
200     phishing_detection_started_ = true;
201 
202     // The callback must be run before destruction, so send a minimal
203     // ClientPhishingRequest.
204     ClientPhishingRequest request;
205     request.set_client_score(0.8);
206     std::move(callback).Run(mojom::PhishingDetectorResult::SUCCESS,
207                             request.SerializeAsString());
208 
209     return;
210   }
211 
CheckMessage(const GURL * url)212   void CheckMessage(const GURL* url) {
213     if (!url) {
214       EXPECT_FALSE(phishing_detection_started_);
215     } else {
216       ASSERT_TRUE(phishing_detection_started_);
217       EXPECT_EQ(*url, url_);
218     }
219   }
220 
CheckModel(const std::string & model)221   void CheckModel(const std::string& model) { EXPECT_EQ(model, model_); }
222 
Reset()223   void Reset() {
224     phishing_detection_started_ = false;
225     url_ = GURL();
226     model_ = "";
227   }
228 
229  private:
230   mojo::ReceiverSet<mojom::PhishingDetector> receivers_;
231   bool phishing_detection_started_ = false;
232   GURL url_;
233   std::string model_ = "";
234 
235   DISALLOW_COPY_AND_ASSIGN(FakePhishingDetector);
236 };
237 
238 class ClientSideDetectionHostTestBase : public ChromeRenderViewHostTestHarness {
239  public:
240   typedef security_interstitials::UnsafeResource UnsafeResource;
241 
ClientSideDetectionHostTestBase(bool is_incognito)242   explicit ClientSideDetectionHostTestBase(bool is_incognito)
243       : is_incognito_(is_incognito) {}
244 
InitTestApi()245   void InitTestApi() {
246     service_manager::InterfaceProvider* remote_interfaces =
247         web_contents()->GetMainFrame()->GetRemoteInterfaces();
248 
249     service_manager::InterfaceProvider::TestApi test_api(remote_interfaces);
250 
251     test_api.SetBinderForName(
252         mojom::PhishingDetector::Name_,
253         base::BindRepeating(&FakePhishingDetector::BindReceiver,
254                             base::Unretained(&fake_phishing_detector_)));
255   }
256 
SetUp()257   void SetUp() override {
258     ChromeRenderViewHostTestHarness::SetUp();
259 
260     if (is_incognito_) {
261       auto incognito_web_contents =
262           content::WebContentsTester::CreateTestWebContents(
263               profile()->GetPrimaryOTRProfile(), nullptr);
264       SetContents(std::move(incognito_web_contents));
265     }
266 
267     InitTestApi();
268 
269     // Inject service classes.
270     csd_service_.reset(new StrictMock<MockClientSideDetectionService>());
271     database_manager_ = new StrictMock<MockSafeBrowsingDatabaseManager>();
272     ui_manager_ = new StrictMock<MockSafeBrowsingUIManager>(
273         // TODO(crbug/925153): Port consumers of the SafeBrowsingService to
274         // use the interface in components/safe_browsing, and remove this cast.
275         static_cast<safe_browsing::SafeBrowsingService*>(
276             SafeBrowsingService::CreateSafeBrowsingService()));
277 
278     csd_host_ = ClientSideDetectionHost::Create(web_contents());
279     csd_host_->set_client_side_detection_service(csd_service_.get());
280     csd_host_->set_ui_manager(ui_manager_.get());
281     csd_host_->set_database_manager(database_manager_.get());
282     csd_host_->set_tick_clock_for_testing(&clock_);
283   }
284 
TearDown()285   void TearDown() override {
286     // Delete the host object on the UI thread and release the
287     // SafeBrowsingService.
288     content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE,
289                                                    csd_host_.release());
290     database_manager_.reset();
291     ui_manager_.reset();
292     base::RunLoop().RunUntilIdle();
293 
294     ChromeRenderViewHostTestHarness::TearDown();
295   }
296 
PhishingDetectionDone(const std::string & verdict_str)297   void PhishingDetectionDone(const std::string& verdict_str) {
298     csd_host_->PhishingDetectionDone(mojom::PhishingDetectorResult::SUCCESS,
299                                      verdict_str);
300   }
301 
PhishingDetectionError(mojom::PhishingDetectorResult error)302   void PhishingDetectionError(mojom::PhishingDetectorResult error) {
303     csd_host_->PhishingDetectionDone(error, "");
304   }
305 
ExpectPreClassificationChecks(const GURL & url,const bool * is_private,const bool * match_csd_whitelist,const bool * get_valid_cached_result,const bool * is_in_cache,const bool * over_phishing_report_limit)306   void ExpectPreClassificationChecks(const GURL& url,
307                                      const bool* is_private,
308                                      const bool* match_csd_whitelist,
309                                      const bool* get_valid_cached_result,
310                                      const bool* is_in_cache,
311                                      const bool* over_phishing_report_limit) {
312     if (is_private) {
313       EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
314           .WillOnce(Return(*is_private));
315     }
316     if (match_csd_whitelist) {
317       EXPECT_CALL(*database_manager_.get(), CheckCsdWhitelistUrl(url, _))
318           .WillOnce(Return(*match_csd_whitelist ? AsyncMatch::MATCH
319                                                 : AsyncMatch::NO_MATCH));
320     }
321     if (get_valid_cached_result) {
322       EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull()))
323           .WillOnce(
324               DoAll(SetArgPointee<1>(true), Return(*get_valid_cached_result)));
325     }
326     if (is_in_cache) {
327       EXPECT_CALL(*csd_service_, IsInCache(url)).WillOnce(Return(*is_in_cache));
328     }
329     if (over_phishing_report_limit) {
330       EXPECT_CALL(*csd_service_, OverPhishingReportLimit())
331           .WillOnce(Return(*over_phishing_report_limit));
332     }
333   }
334 
WaitAndCheckPreClassificationChecks()335   void WaitAndCheckPreClassificationChecks() {
336     // Wait for CheckCsdWhitelist and CheckCache() to be called if at all.
337     base::RunLoop().RunUntilIdle();
338     EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
339     EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
340     EXPECT_TRUE(Mock::VerifyAndClear(database_manager_.get()));
341   }
342 
NavigateAndCommit(const GURL & safe_url)343   void NavigateAndCommit(const GURL& safe_url) {
344     controller().LoadURL(
345         safe_url, content::Referrer(), ui::PAGE_TRANSITION_LINK,
346         std::string());
347 
348     content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
349   }
350 
AdvanceTimeTickClock(base::TimeDelta delta)351   void AdvanceTimeTickClock(base::TimeDelta delta) { clock_.Advance(delta); }
352 
353  protected:
354   std::unique_ptr<ClientSideDetectionHost> csd_host_;
355   std::unique_ptr<StrictMock<MockClientSideDetectionService>> csd_service_;
356   scoped_refptr<StrictMock<MockSafeBrowsingUIManager> > ui_manager_;
357   scoped_refptr<StrictMock<MockSafeBrowsingDatabaseManager> > database_manager_;
358   FakePhishingDetector fake_phishing_detector_;
359   base::SimpleTestTickClock clock_;
360   const bool is_incognito_;
361 };
362 
363 class ClientSideDetectionHostTest : public ClientSideDetectionHostTestBase {
364  public:
ClientSideDetectionHostTest()365   ClientSideDetectionHostTest()
366       : ClientSideDetectionHostTestBase(false /*is_incognito*/) {}
367 };
368 
369 class ClientSideDetectionHostIncognitoTest
370     : public ClientSideDetectionHostTestBase {
371  public:
ClientSideDetectionHostIncognitoTest()372   ClientSideDetectionHostIncognitoTest()
373       : ClientSideDetectionHostTestBase(true /*is_incognito*/) {}
374 };
375 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneInvalidVerdict)376 TEST_F(ClientSideDetectionHostTest, PhishingDetectionDoneInvalidVerdict) {
377   // Case 0: renderer sends an invalid verdict string that we're unable to
378   // parse.
379   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
380       .Times(0);
381   PhishingDetectionDone("Invalid Protocol Buffer");
382   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
383 }
384 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneNotPhishing)385 TEST_F(ClientSideDetectionHostTest, PhishingDetectionDoneNotPhishing) {
386   // Case 1: client thinks the page is phishing.  The server does not agree.
387   // No interstitial is shown.
388   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
389   ClientPhishingRequest verdict;
390   verdict.set_url("http://phishingurl.com/");
391   verdict.set_client_score(1.0f);
392   verdict.set_is_phishing(true);
393 
394   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
395                                  PartiallyEqualVerdict(verdict), _, _, _))
396       .WillOnce(MoveArg<3>(&cb));
397   PhishingDetectionDone(verdict.SerializeAsString());
398   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
399   ASSERT_FALSE(cb.is_null());
400 
401   // Make sure DisplayBlockingPage is not going to be called.
402   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
403   std::move(cb).Run(GURL(verdict.url()), false);
404   base::RunLoop().RunUntilIdle();
405   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
406 }
407 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneDisabled)408 TEST_F(ClientSideDetectionHostTest, PhishingDetectionDoneDisabled) {
409   // Case 2: client thinks the page is phishing and so does the server but
410   // showing the interstitial is disabled => no interstitial is shown.
411   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
412   ClientPhishingRequest verdict;
413   verdict.set_url("http://phishingurl.com/");
414   verdict.set_client_score(1.0f);
415   verdict.set_is_phishing(true);
416 
417   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
418                                  PartiallyEqualVerdict(verdict), _, _, _))
419       .WillOnce(MoveArg<3>(&cb));
420   PhishingDetectionDone(verdict.SerializeAsString());
421   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
422   ASSERT_FALSE(cb.is_null());
423 
424   // Make sure DisplayBlockingPage is not going to be called.
425   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)).Times(0);
426   std::move(cb).Run(GURL(verdict.url()), false);
427   base::RunLoop().RunUntilIdle();
428   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
429 }
430 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneShowInterstitial)431 TEST_F(ClientSideDetectionHostTest, PhishingDetectionDoneShowInterstitial) {
432   // Case 3: client thinks the page is phishing and so does the server.
433   // We show an interstitial.
434   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
435   GURL phishing_url("http://phishingurl.com/");
436   ClientPhishingRequest verdict;
437   verdict.set_url(phishing_url.spec());
438   verdict.set_client_score(1.0f);
439   verdict.set_is_phishing(true);
440 
441   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
442                                  PartiallyEqualVerdict(verdict), _, _, _))
443       .WillOnce(MoveArg<3>(&cb));
444   PhishingDetectionDone(verdict.SerializeAsString());
445   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
446   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
447   ASSERT_FALSE(cb.is_null());
448 
449   UnsafeResource resource;
450   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
451       .WillOnce(SaveArg<0>(&resource));
452   std::move(cb).Run(phishing_url, true);
453 
454   base::RunLoop().RunUntilIdle();
455   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
456   EXPECT_EQ(phishing_url, resource.url);
457   EXPECT_EQ(phishing_url, resource.original_url);
458   EXPECT_FALSE(resource.is_subresource);
459   EXPECT_EQ(SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING, resource.threat_type);
460   EXPECT_EQ(ThreatSource::CLIENT_SIDE_DETECTION, resource.threat_source);
461   EXPECT_EQ(web_contents(), resource.web_contents_getter.Run());
462 
463   // Make sure the client object will be deleted.
464   content::GetIOThreadTaskRunner({})->PostTask(
465       FROM_HERE,
466       base::BindOnce(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
467                      ui_manager_, resource.callback));
468 }
469 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneMultiplePings)470 TEST_F(ClientSideDetectionHostTest, PhishingDetectionDoneMultiplePings) {
471   // Case 4 & 5: client thinks a page is phishing then navigates to
472   // another page which is also considered phishing by the client
473   // before the server responds with a verdict.  After a while the
474   // server responds for both requests with a phishing verdict.  Only
475   // a single interstitial is shown for the second URL.
476   ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
477   GURL phishing_url("http://phishingurl.com/");
478   ClientPhishingRequest verdict;
479   verdict.set_url(phishing_url.spec());
480   verdict.set_client_score(1.0f);
481   verdict.set_is_phishing(true);
482 
483   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
484                                  PartiallyEqualVerdict(verdict), _, _, _))
485       .WillOnce(MoveArg<3>(&cb));
486   PhishingDetectionDone(verdict.SerializeAsString());
487   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
488   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
489   ASSERT_FALSE(cb.is_null());
490 
491   GURL other_phishing_url("http://other_phishing_url.com/bla");
492   ExpectPreClassificationChecks(other_phishing_url, &kFalse, &kFalse, &kFalse,
493                                 &kFalse, &kFalse);
494   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
495   // We navigate away.  The callback cb should be revoked.
496   NavigateAndCommit(other_phishing_url);
497   // Wait for the pre-classification checks to finish for other_phishing_url.
498   WaitAndCheckPreClassificationChecks();
499 
500   ClientSideDetectionService::ClientReportPhishingRequestCallback cb_other;
501   verdict.set_url(other_phishing_url.spec());
502   verdict.set_client_score(0.8f);
503   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(
504                                  PartiallyEqualVerdict(verdict), _, _, _))
505       .WillOnce(MoveArg<3>(&cb_other));
506   PhishingDetectionDone(verdict.SerializeAsString());
507   base::RunLoop().RunUntilIdle();
508   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
509   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
510   ASSERT_FALSE(cb_other.is_null());
511 
512   // We expect that the interstitial is shown for the second phishing URL and
513   // not for the first phishing URL.
514   UnsafeResource resource;
515   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
516       .WillOnce(SaveArg<0>(&resource));
517 
518   std::move(cb).Run(phishing_url, true);  // Should have no effect.
519   std::move(cb_other).Run(other_phishing_url,
520                           true);  // Should show interstitial.
521 
522   base::RunLoop().RunUntilIdle();
523   EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
524   EXPECT_EQ(other_phishing_url, resource.url);
525   EXPECT_EQ(other_phishing_url, resource.original_url);
526   EXPECT_FALSE(resource.is_subresource);
527   EXPECT_EQ(SB_THREAT_TYPE_URL_CLIENT_SIDE_PHISHING, resource.threat_type);
528   EXPECT_EQ(ThreatSource::CLIENT_SIDE_DETECTION, resource.threat_source);
529   EXPECT_EQ(web_contents(), resource.web_contents_getter.Run());
530 
531   // Make sure the client object will be deleted.
532   content::GetIOThreadTaskRunner({})->PostTask(
533       FROM_HERE,
534       base::BindOnce(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
535                      ui_manager_, resource.callback));
536 }
537 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneVerdictNotPhishing)538 TEST_F(ClientSideDetectionHostTest, PhishingDetectionDoneVerdictNotPhishing) {
539   // Case 6: renderer sends a verdict string that isn't phishing.
540   ClientPhishingRequest verdict;
541   verdict.set_url("http://not-phishing.com/");
542   verdict.set_client_score(0.1f);
543   verdict.set_is_phishing(false);
544 
545   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
546       .Times(0);
547   PhishingDetectionDone(verdict.SerializeAsString());
548   EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
549 }
550 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneVerdictNotPhishingButSBMatchSubResource)551 TEST_F(ClientSideDetectionHostTest,
552        PhishingDetectionDoneVerdictNotPhishingButSBMatchSubResource) {
553   // Case 7: renderer sends a verdict string that isn't phishing but the URL
554   // of a subresource was on the regular phishing or malware lists.
555   GURL url("http://not-phishing.com/");
556   ClientPhishingRequest verdict;
557   verdict.set_url(url.spec());
558   verdict.set_client_score(0.1f);
559   verdict.set_is_phishing(false);
560 
561   // First we have to navigate to the URL to set the unique page ID.
562   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
563   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
564                                 &kFalse);
565   NavigateAndCommit(url);
566   WaitAndCheckPreClassificationChecks();
567 
568   PhishingDetectionDone(verdict.SerializeAsString());
569 }
570 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneVerdictNotPhishingButSBMatchOnNewRVH)571 TEST_F(ClientSideDetectionHostTest,
572        PhishingDetectionDoneVerdictNotPhishingButSBMatchOnNewRVH) {
573   // When navigating to a different host (thus creating a pending RVH) which
574   // matches regular malware list, and after navigation the renderer sends a
575   // verdict string that isn't phishing, we should still send the report.
576 
577   // Do an initial navigation to a safe host.
578   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
579   GURL start_url("http://safe.example.com/");
580   ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse,
581                                 &kFalse);
582   NavigateAndCommit(start_url);
583   WaitAndCheckPreClassificationChecks();
584 
585   // Now navigate to a different host which will have a malware hit before the
586   // navigation commits.
587   GURL url("http://malware-but-not-phishing.com/");
588   ClientPhishingRequest verdict;
589   verdict.set_url(url.spec());
590   verdict.set_client_score(0.1f);
591   verdict.set_is_phishing(false);
592 
593   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
594   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
595                                 &kFalse);
596   NavigateAndCommit(url);
597   WaitAndCheckPreClassificationChecks();
598 
599   PhishingDetectionDone(verdict.SerializeAsString());
600 }
601 
TEST_F(ClientSideDetectionHostTest,PhishingDetectionDoneVerdictNotPhishingButSBMatchOnSubresourceWhileNavPending)602 TEST_F(
603     ClientSideDetectionHostTest,
604     PhishingDetectionDoneVerdictNotPhishingButSBMatchOnSubresourceWhileNavPending) {
605   // When a malware hit happens on a committed page while a slow pending load is
606   // in progress, the csd report should be sent for the committed page.
607 
608   // Do an initial navigation to a safe host.
609   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
610   GURL start_url("http://safe.example.com/");
611   ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse,
612                                 &kFalse);
613   NavigateAndCommit(start_url);
614   WaitAndCheckPreClassificationChecks();
615 
616   // Now navigate to a different host which does not have a SB hit.
617   GURL url("http://not-malware-not-phishing-but-malware-subresource.com/");
618   ClientPhishingRequest verdict;
619   verdict.set_url(url.spec());
620   verdict.set_client_score(0.1f);
621   verdict.set_is_phishing(false);
622 
623   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
624   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
625                                 &kFalse);
626   NavigateAndCommit(url);
627 
628   // Create a pending navigation, but don't commit it.
629   GURL pending_url("http://slow.example.com/");
630   auto pending_navigation =
631       content::NavigationSimulator::CreateBrowserInitiated(pending_url,
632                                                            web_contents());
633   pending_navigation->Start();
634 
635   WaitAndCheckPreClassificationChecks();
636 
637   PhishingDetectionDone(verdict.SerializeAsString());
638 }
639 
640 // This test doesn't work because it makes assumption about how
641 // the message loop is run, and those assumptions are wrong when properly
642 // simulating a navigation with browser-side navigations.
643 // TODO(clamy): Fix the test and re-enable. See crbug.com/753357.
TEST_F(ClientSideDetectionHostTest,DISABLED_NavigationCancelsShouldClassifyUrl)644 TEST_F(ClientSideDetectionHostTest,
645        DISABLED_NavigationCancelsShouldClassifyUrl) {
646   // Test that canceling pending should classify requests works as expected.
647   GURL first_url("http://first.phishy.url.com");
648   GURL second_url("http://second.url.com/");
649   // The first few checks are done synchronously so check that they have been
650   // done for the first URL, while the second URL has all the checks done.  We
651   // need to manually set up the IsPrivateIPAddress mock since if the same mock
652   // expectation is specified twice, gmock will only use the last instance of
653   // it, meaning the first will never be matched.
654   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
655   EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
656       .WillOnce(Return(false))
657       .WillOnce(Return(false));
658   ExpectPreClassificationChecks(first_url, nullptr, &kFalse, nullptr, nullptr,
659                                 nullptr);
660   ExpectPreClassificationChecks(second_url, nullptr, &kFalse, &kFalse, &kFalse,
661                                 &kFalse);
662 
663   NavigateAndCommit(first_url);
664   // Don't flush the message loop, as we want to navigate to a different
665   // url before the final pre-classification checks are run.
666   NavigateAndCommit(second_url);
667   WaitAndCheckPreClassificationChecks();
668 }
669 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckPass)670 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckPass) {
671   // Navigate the tab to a page.  We should see a StartPhishingDetection IPC.
672   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
673   GURL url("http://host.com/");
674   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
675                                 &kFalse);
676   NavigateAndKeepLoading(web_contents(), url);
677   WaitAndCheckPreClassificationChecks();
678 
679   fake_phishing_detector_.CheckMessage(&url);
680 }
681 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckMatchWhitelist)682 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckMatchWhitelist) {
683   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
684   GURL url("http://host.com/");
685   ExpectPreClassificationChecks(url, &kFalse, &kTrue, nullptr, nullptr,
686                                 nullptr);
687   NavigateAndKeepLoading(web_contents(), url);
688   WaitAndCheckPreClassificationChecks();
689 }
690 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckSameDocumentNavigation)691 TEST_F(ClientSideDetectionHostTest,
692        TestPreClassificationCheckSameDocumentNavigation) {
693   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
694   GURL url("http://host.com/");
695   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
696                                 &kFalse);
697   NavigateAndKeepLoading(web_contents(), url);
698   WaitAndCheckPreClassificationChecks();
699 
700   fake_phishing_detector_.CheckMessage(&url);
701   fake_phishing_detector_.Reset();
702 
703   // Now try an same-document navigation.  This should not trigger an IPC.
704   EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0);
705   GURL inpage("http://host.com/#foo");
706   ExpectPreClassificationChecks(inpage, nullptr, nullptr, nullptr, nullptr,
707                                 nullptr);
708   NavigateAndKeepLoading(web_contents(), inpage);
709   WaitAndCheckPreClassificationChecks();
710 
711   fake_phishing_detector_.CheckMessage(nullptr);
712 }
713 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckXHTML)714 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckXHTML) {
715   // Check that XHTML is supported, in addition to the default HTML type.
716   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
717   GURL url("http://host.com/xhtml");
718   auto navigation =
719       content::NavigationSimulator::CreateBrowserInitiated(url, web_contents());
720   navigation->SetContentsMimeType("application/xhtml+xml");
721   navigation->SetKeepLoading(true);
722   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
723                                 &kFalse);
724   navigation->Commit();
725   WaitAndCheckPreClassificationChecks();
726 
727   fake_phishing_detector_.CheckMessage(&url);
728 }
729 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckTwoNavigations)730 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckTwoNavigations) {
731   // Navigate to two hosts, which should cause two IPCs.
732   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
733   GURL url1("http://host1.com/");
734   ExpectPreClassificationChecks(url1, &kFalse, &kFalse, &kFalse, &kFalse,
735                                 &kFalse);
736   NavigateAndKeepLoading(web_contents(), url1);
737   WaitAndCheckPreClassificationChecks();
738 
739   fake_phishing_detector_.CheckMessage(&url1);
740 
741   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
742   GURL url2("http://host2.com/");
743   ExpectPreClassificationChecks(url2, &kFalse, &kFalse, &kFalse, &kFalse,
744                                 &kFalse);
745   NavigateAndKeepLoading(web_contents(), url2);
746   // Re-override the binder for PhishingDetector because navigation causes
747   // a new web InterfaceProvider to be created
748   InitTestApi();
749   WaitAndCheckPreClassificationChecks();
750 
751   fake_phishing_detector_.CheckMessage(&url2);
752 }
753 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckPrivateIpAddress)754 TEST_F(ClientSideDetectionHostTest,
755        TestPreClassificationCheckPrivateIpAddress) {
756   // If IsPrivateIPAddress returns true, no IPC should be triggered.
757   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
758   GURL url("http://host3.com/");
759   ExpectPreClassificationChecks(url, &kTrue, nullptr, nullptr, nullptr,
760                                 nullptr);
761   NavigateAndCommit(url);
762   WaitAndCheckPreClassificationChecks();
763 
764   fake_phishing_detector_.CheckMessage(nullptr);
765 }
766 
TEST_F(ClientSideDetectionHostIncognitoTest,TestPreClassificationCheckIncognito)767 TEST_F(ClientSideDetectionHostIncognitoTest,
768        TestPreClassificationCheckIncognito) {
769   // If the tab is incognito there should be no IPC.  Also, we shouldn't
770   // even check the csd-whitelist.
771   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
772   GURL url("http://host4.com/");
773   ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr,
774                                 nullptr);
775 
776   content::WebContentsTester::For(web_contents())->NavigateAndCommit(url);
777   WaitAndCheckPreClassificationChecks();
778 
779   fake_phishing_detector_.CheckMessage(nullptr);
780 }
781 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckInvalidCache)782 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckInvalidCache) {
783   // If item is in the cache but it isn't valid, we will classify regardless
784   // of whether we are over the reporting limit.
785   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
786   GURL url("http://host6.com/");
787   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue,
788                                 nullptr);
789 
790   NavigateAndKeepLoading(web_contents(), url);
791   WaitAndCheckPreClassificationChecks();
792 
793   fake_phishing_detector_.CheckMessage(&url);
794 }
795 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckOverPhishingReportingLimit)796 TEST_F(ClientSideDetectionHostTest,
797        TestPreClassificationCheckOverPhishingReportingLimit) {
798   // If the url isn't in the cache and we are over the reporting limit, we
799   // don't do classification.
800   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
801   GURL url("http://host7.com/");
802   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
803                                 &kTrue);
804   NavigateAndKeepLoading(web_contents(), url);
805   WaitAndCheckPreClassificationChecks();
806 
807   fake_phishing_detector_.CheckMessage(nullptr);
808 }
809 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckOverBothReportingLimits)810 TEST_F(ClientSideDetectionHostTest,
811        TestPreClassificationCheckOverBothReportingLimits) {
812   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
813   GURL url("http://host.com/");
814   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
815                                 &kTrue);
816   NavigateAndKeepLoading(web_contents(), url);
817   WaitAndCheckPreClassificationChecks();
818 
819   fake_phishing_detector_.CheckMessage(nullptr);
820 }
821 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckHttpsUrl)822 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckHttpsUrl) {
823   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
824   GURL url("https://host.com/");
825   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
826                                 &kFalse);
827   NavigateAndKeepLoading(web_contents(), url);
828   WaitAndCheckPreClassificationChecks();
829 
830   fake_phishing_detector_.CheckMessage(&url);
831 }
832 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckNoneHttpOrHttpsUrl)833 TEST_F(ClientSideDetectionHostTest,
834        TestPreClassificationCheckNoneHttpOrHttpsUrl) {
835   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
836   GURL url("file://host.com/");
837   ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr,
838                                 nullptr);
839   NavigateAndKeepLoading(web_contents(), url);
840   WaitAndCheckPreClassificationChecks();
841 
842   fake_phishing_detector_.CheckMessage(nullptr);
843 }
844 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationCheckValidCached)845 TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckValidCached) {
846   // If result is cached, we will try and display the blocking page directly
847   // with no start classification message.
848   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
849   GURL url("http://host8.com/");
850   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kFalse,
851                                 &kFalse);
852 
853   UnsafeResource resource;
854   EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
855       .WillOnce(SaveArg<0>(&resource));
856 
857   NavigateAndKeepLoading(web_contents(), url);
858   WaitAndCheckPreClassificationChecks();
859   EXPECT_EQ(url, resource.url);
860   EXPECT_EQ(url, resource.original_url);
861 
862   fake_phishing_detector_.CheckMessage(nullptr);
863 }
864 
TEST_F(ClientSideDetectionHostTest,TestPreClassificationWhitelistedByPolicy)865 TEST_F(ClientSideDetectionHostTest, TestPreClassificationWhitelistedByPolicy) {
866   // Configures enterprise whitelist.
867   ListPrefUpdate update(profile()->GetPrefs(),
868                         prefs::kSafeBrowsingWhitelistDomains);
869   update->AppendString("example.com");
870 
871   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
872   GURL url("http://example.com/");
873   ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr,
874                                 nullptr);
875 
876   NavigateAndKeepLoading(web_contents(), url);
877   WaitAndCheckPreClassificationChecks();
878 
879   fake_phishing_detector_.CheckMessage(nullptr);
880 }
881 
TEST_F(ClientSideDetectionHostTest,RecordsPhishingDetectorResults)882 TEST_F(ClientSideDetectionHostTest, RecordsPhishingDetectorResults) {
883   {
884     ClientPhishingRequest verdict;
885     verdict.set_url("http://not-phishing.com/");
886     verdict.set_client_score(0.1f);
887     verdict.set_is_phishing(false);
888 
889     base::HistogramTester histogram_tester;
890 
891     EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
892         .Times(0);
893     PhishingDetectionDone(verdict.SerializeAsString());
894     EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
895 
896     histogram_tester.ExpectUniqueSample(
897         "SBClientPhishing.PhishingDetectorResult",
898         mojom::PhishingDetectorResult::SUCCESS, 1);
899   }
900 
901   {
902     base::HistogramTester histogram_tester;
903 
904     EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
905         .Times(0);
906     PhishingDetectionError(mojom::PhishingDetectorResult::CLASSIFIER_NOT_READY);
907     EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
908 
909     histogram_tester.ExpectUniqueSample(
910         "SBClientPhishing.PhishingDetectorResult",
911         mojom::PhishingDetectorResult::CLASSIFIER_NOT_READY, 1);
912   }
913 
914   {
915     base::HistogramTester histogram_tester;
916 
917     EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
918         .Times(0);
919     PhishingDetectionError(
920         mojom::PhishingDetectorResult::FORWARD_BACK_TRANSITION);
921     EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
922 
923     histogram_tester.ExpectUniqueSample(
924         "SBClientPhishing.PhishingDetectorResult",
925         mojom::PhishingDetectorResult::FORWARD_BACK_TRANSITION, 1);
926   }
927 }
928 
TEST_F(ClientSideDetectionHostTest,RecordsPhishingDetectionDuration)929 TEST_F(ClientSideDetectionHostTest, RecordsPhishingDetectionDuration) {
930   base::HistogramTester histogram_tester;
931   histogram_tester.ExpectTotalCount(
932       "SBClientPhishing.PhishingDetectionDuration", 0);
933 
934   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
935   GURL start_url("http://safe.example.com/");
936   ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse,
937                                 &kFalse);
938   NavigateAndCommit(start_url);
939   WaitAndCheckPreClassificationChecks();
940   histogram_tester.ExpectTotalCount(
941       "SBClientPhishing.PhishingDetectionDuration", 1);
942 
943   GURL url("http://phishing.example.com/");
944   ClientPhishingRequest verdict;
945   verdict.set_url(url.spec());
946   verdict.set_client_score(0.1f);
947   verdict.set_is_phishing(false);
948 
949   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("model_str"));
950   ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
951                                 &kFalse);
952   NavigateAndCommit(url);
953   WaitAndCheckPreClassificationChecks();
954   const base::TimeDelta duration = base::TimeDelta::FromMilliseconds(10);
955   AdvanceTimeTickClock(duration);
956 
957   PhishingDetectionDone(verdict.SerializeAsString());
958 
959   histogram_tester.ExpectTotalCount(
960       "SBClientPhishing.PhishingDetectionDuration", 3);
961   EXPECT_LE(duration.InMilliseconds(),
962             histogram_tester
963                 .GetAllSamples("SBClientPhishing.PhishingDetectionDuration")
964                 .front()
965                 .min);
966 }
967 
TEST_F(ClientSideDetectionHostTest,TestSendModelToRenderFrame)968 TEST_F(ClientSideDetectionHostTest, TestSendModelToRenderFrame) {
969   StrictMock<MockModelLoader> loader;
970   loader.SetModelStrForTesting("standard");
971   EXPECT_CALL(*csd_service_, GetModelStr()).WillRepeatedly(Return("standard"));
972   csd_host_->SendModelToRenderFrame();
973   base::RunLoop().RunUntilIdle();
974   fake_phishing_detector_.CheckModel("standard");
975   fake_phishing_detector_.Reset();
976 }
977 
TEST_F(ClientSideDetectionHostTest,ClearsScreenshotData)978 TEST_F(ClientSideDetectionHostTest, ClearsScreenshotData) {
979   profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
980                                     false);
981 
982   ClientPhishingRequest verdict;
983   verdict.set_url("http://phishingurl.com/");
984   verdict.set_client_score(1.0f);
985   verdict.set_is_phishing(true);
986   verdict.set_screenshot_digest("screenshot_digest");
987   verdict.set_screenshot_phash("screenshot_phash");
988   verdict.set_phash_dimension_size(48);
989 
990   ClientPhishingRequest request;
991 
992   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
993       .WillOnce(testing::SaveArgPointee<0>(&request));
994   PhishingDetectionDone(verdict.SerializeAsString());
995   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
996   EXPECT_FALSE(request.has_phash_dimension_size());
997   EXPECT_FALSE(request.has_screenshot_phash());
998   EXPECT_FALSE(request.has_screenshot_digest());
999 }
1000 
TEST_F(ClientSideDetectionHostTest,AllowsScreenshotDataForSBER)1001 TEST_F(ClientSideDetectionHostTest, AllowsScreenshotDataForSBER) {
1002   profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
1003                                     true);
1004 
1005   ClientPhishingRequest verdict;
1006   verdict.set_url("http://phishingurl.com/");
1007   verdict.set_client_score(1.0f);
1008   verdict.set_is_phishing(true);
1009   verdict.set_screenshot_digest("screenshot_digest");
1010   verdict.set_screenshot_phash("screenshot_phash");
1011   verdict.set_phash_dimension_size(48);
1012 
1013   ClientPhishingRequest request;
1014 
1015   EXPECT_CALL(*csd_service_, SendClientReportPhishingRequest(_, _, _, _))
1016       .WillOnce(testing::SaveArgPointee<0>(&request));
1017   PhishingDetectionDone(verdict.SerializeAsString());
1018   EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
1019   EXPECT_TRUE(request.has_phash_dimension_size());
1020   EXPECT_TRUE(request.has_screenshot_phash());
1021   EXPECT_TRUE(request.has_screenshot_digest());
1022 }
1023 
1024 }  // namespace safe_browsing
1025