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