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.
10 #include "base/callback.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
16 #include "chrome/common/safe_browsing/client_model.pb.h"
17 #include "chrome/common/safe_browsing/csd.pb.h"
18 #include "content/public/test/test_browser_thread.h"
19 #include "crypto/sha2.h"
20 #include "net/http/http_status_code.h"
21 #include "net/url_request/test_url_fetcher_factory.h"
22 #include "net/url_request/url_request_status.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
27 using ::testing::Invoke
;
28 using ::testing::Mock
;
29 using ::testing::StrictMock
;
31 using content::BrowserThread
;
33 namespace safe_browsing
{
35 class MockClientSideDetectionService
: public ClientSideDetectionService
{
37 MockClientSideDetectionService() : ClientSideDetectionService(NULL
) {}
38 virtual ~MockClientSideDetectionService() {}
40 MOCK_METHOD1(EndFetchModel
, void(ClientModelStatus
));
41 MOCK_METHOD1(ScheduleFetchModel
, void(int64
));
43 void Schedule(int64
) {
44 // Ignore the delay when testing.
50 SetEnabledAndRefreshState(false);
54 DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService
);
57 ACTION(QuitCurrentMessageLoop
) {
58 base::MessageLoop::current()->Quit();
63 class ClientSideDetectionServiceTest
: public testing::Test
{
65 virtual void SetUp() {
66 file_thread_
.reset(new content::TestBrowserThread(BrowserThread::FILE,
69 factory_
.reset(new net::FakeURLFetcherFactory(NULL
));
71 browser_thread_
.reset(new content::TestBrowserThread(BrowserThread::UI
,
75 virtual void TearDown() {
76 msg_loop_
.RunUntilIdle();
79 browser_thread_
.reset();
82 bool SendClientReportPhishingRequest(const GURL
& phishing_url
,
84 ClientPhishingRequest
* request
= new ClientPhishingRequest();
85 request
->set_url(phishing_url
.spec());
86 request
->set_client_score(score
);
87 request
->set_is_phishing(true); // client thinks the URL is phishing.
88 csd_service_
->SendClientReportPhishingRequest(
90 base::Bind(&ClientSideDetectionServiceTest::SendRequestDone
,
91 base::Unretained(this)));
92 phishing_url_
= phishing_url
;
93 msg_loop_
.Run(); // Waits until callback is called.
97 bool SendClientReportMalwareRequest(const GURL
& url
) {
98 scoped_ptr
<ClientMalwareRequest
> request(new ClientMalwareRequest());
99 request
->set_url(url
.spec());
100 csd_service_
->SendClientReportMalwareRequest(
102 base::Bind(&ClientSideDetectionServiceTest::SendMalwareRequestDone
,
103 base::Unretained(this)));
105 msg_loop_
.Run(); // Waits until callback is called.
109 void SetModelFetchResponse(std::string response_data
,
110 net::HttpStatusCode response_code
,
111 net::URLRequestStatus::Status status
) {
112 factory_
->SetFakeResponse(GURL(ClientSideDetectionService::kClientModelUrl
),
113 response_data
, response_code
, status
);
116 void SetClientReportPhishingResponse(std::string response_data
,
117 net::HttpStatusCode response_code
,
118 net::URLRequestStatus::Status status
) {
119 factory_
->SetFakeResponse(
120 ClientSideDetectionService::GetClientReportUrl(
121 ClientSideDetectionService::kClientReportPhishingUrl
),
122 response_data
, response_code
, status
);
125 void SetClientReportMalwareResponse(std::string response_data
,
126 net::HttpStatusCode response_code
,
127 net::URLRequestStatus::Status status
) {
128 factory_
->SetFakeResponse(
129 ClientSideDetectionService::GetClientReportUrl(
130 ClientSideDetectionService::kClientReportMalwareUrl
),
131 response_data
, response_code
, status
);
134 int GetNumReports(std::queue
<base::Time
>* report_times
) {
135 return csd_service_
->GetNumReports(report_times
);
138 std::queue
<base::Time
>& GetPhishingReportTimes() {
139 return csd_service_
->phishing_report_times_
;
142 std::queue
<base::Time
>& GetMalwareReportTimes() {
143 return csd_service_
->malware_report_times_
;
146 void SetCache(const GURL
& gurl
, bool is_phishing
, base::Time time
) {
147 csd_service_
->cache_
[gurl
] =
148 make_linked_ptr(new ClientSideDetectionService::CacheState(is_phishing
,
153 ClientSideDetectionService::PhishingCache
& cache
= csd_service_
->cache_
;
154 base::Time now
= base::Time::Now();
156 now
- base::TimeDelta::FromDays(
157 ClientSideDetectionService::kNegativeCacheIntervalDays
) +
158 base::TimeDelta::FromMinutes(5);
159 cache
[GURL("http://first.url.com/")] =
160 make_linked_ptr(new ClientSideDetectionService::CacheState(false,
164 now
- base::TimeDelta::FromDays(
165 ClientSideDetectionService::kNegativeCacheIntervalDays
) -
166 base::TimeDelta::FromHours(1);
167 cache
[GURL("http://second.url.com/")] =
168 make_linked_ptr(new ClientSideDetectionService::CacheState(false,
172 now
- base::TimeDelta::FromMinutes(
173 ClientSideDetectionService::kPositiveCacheIntervalMinutes
) -
174 base::TimeDelta::FromMinutes(5);
175 cache
[GURL("http://third.url.com/")] =
176 make_linked_ptr(new ClientSideDetectionService::CacheState(true, time
));
179 now
- base::TimeDelta::FromMinutes(
180 ClientSideDetectionService::kPositiveCacheIntervalMinutes
) +
181 base::TimeDelta::FromMinutes(5);
182 cache
[GURL("http://fourth.url.com/")] =
183 make_linked_ptr(new ClientSideDetectionService::CacheState(true, time
));
185 csd_service_
->UpdateCache();
187 // 3 elements should be in the cache, the first, third, and fourth.
188 EXPECT_EQ(3U, cache
.size());
189 EXPECT_TRUE(cache
.find(GURL("http://first.url.com/")) != cache
.end());
190 EXPECT_TRUE(cache
.find(GURL("http://third.url.com/")) != cache
.end());
191 EXPECT_TRUE(cache
.find(GURL("http://fourth.url.com/")) != cache
.end());
193 // While 3 elements remain, only the first and the fourth are actually
196 EXPECT_TRUE(csd_service_
->GetValidCachedResult(
197 GURL("http://first.url.com"), &is_phishing
));
198 EXPECT_FALSE(is_phishing
);
199 EXPECT_FALSE(csd_service_
->GetValidCachedResult(
200 GURL("http://third.url.com"), &is_phishing
));
201 EXPECT_TRUE(csd_service_
->GetValidCachedResult(
202 GURL("http://fourth.url.com"), &is_phishing
));
203 EXPECT_TRUE(is_phishing
);
206 void AddFeature(const std::string
& name
, double value
,
207 ClientPhishingRequest
* request
) {
208 ClientPhishingRequest_Feature
* feature
= request
->add_feature_map();
209 feature
->set_name(name
);
210 feature
->set_value(value
);
213 void AddNonModelFeature(const std::string
& name
, double value
,
214 ClientPhishingRequest
* request
) {
215 ClientPhishingRequest_Feature
* feature
=
216 request
->add_non_model_feature_map();
217 feature
->set_name(name
);
218 feature
->set_value(value
);
221 void CheckConfirmedMalwareUrl(GURL url
) {
222 ASSERT_EQ(confirmed_malware_url_
, url
);
226 scoped_ptr
<ClientSideDetectionService
> csd_service_
;
227 scoped_ptr
<net::FakeURLFetcherFactory
> factory_
;
228 base::MessageLoop msg_loop_
;
231 void SendRequestDone(GURL phishing_url
, bool is_phishing
) {
232 ASSERT_EQ(phishing_url
, phishing_url_
);
233 is_phishing_
= is_phishing
;
237 void SendMalwareRequestDone(GURL original_url
, GURL malware_url
,
239 ASSERT_EQ(phishing_url_
, original_url
);
240 confirmed_malware_url_
= malware_url
;
241 is_malware_
= is_malware
;
245 scoped_ptr
<content::TestBrowserThread
> browser_thread_
;
246 scoped_ptr
<content::TestBrowserThread
> file_thread_
;
249 GURL confirmed_malware_url_
;
254 TEST_F(ClientSideDetectionServiceTest
, FetchModelTest
) {
255 // We don't want to use a real service class here because we can't call
256 // the real EndFetchModel. It would reschedule a reload which might
257 // make the test flaky.
258 MockClientSideDetectionService service
;
259 EXPECT_CALL(service
, ScheduleFetchModel(_
)).Times(1);
260 service
.SetEnabledAndRefreshState(true);
262 // The model fetch failed.
263 SetModelFetchResponse("blamodel", net::HTTP_INTERNAL_SERVER_ERROR
,
264 net::URLRequestStatus::FAILED
);
265 EXPECT_CALL(service
, EndFetchModel(
266 ClientSideDetectionService::MODEL_FETCH_FAILED
))
267 .WillOnce(QuitCurrentMessageLoop());
268 service
.StartFetchModel();
269 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
270 Mock::VerifyAndClearExpectations(&service
);
273 SetModelFetchResponse(std::string(), net::HTTP_OK
,
274 net::URLRequestStatus::SUCCESS
);
275 EXPECT_CALL(service
, EndFetchModel(ClientSideDetectionService::MODEL_EMPTY
))
276 .WillOnce(QuitCurrentMessageLoop());
277 service
.StartFetchModel();
278 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
279 Mock::VerifyAndClearExpectations(&service
);
281 // Model is too large.
282 SetModelFetchResponse(
283 std::string(ClientSideDetectionService::kMaxModelSizeBytes
+ 1, 'x'),
284 net::HTTP_OK
, net::URLRequestStatus::SUCCESS
);
285 EXPECT_CALL(service
, EndFetchModel(
286 ClientSideDetectionService::MODEL_TOO_LARGE
))
287 .WillOnce(QuitCurrentMessageLoop());
288 service
.StartFetchModel();
289 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
290 Mock::VerifyAndClearExpectations(&service
);
292 // Unable to parse the model file.
293 SetModelFetchResponse("Invalid model file", net::HTTP_OK
,
294 net::URLRequestStatus::SUCCESS
);
295 EXPECT_CALL(service
, EndFetchModel(
296 ClientSideDetectionService::MODEL_PARSE_ERROR
))
297 .WillOnce(QuitCurrentMessageLoop());
298 service
.StartFetchModel();
299 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
300 Mock::VerifyAndClearExpectations(&service
);
302 // Model that is missing some required fields (missing the version field).
303 ClientSideModel model
;
304 model
.set_max_words_per_term(4);
305 SetModelFetchResponse(model
.SerializePartialAsString(), net::HTTP_OK
,
306 net::URLRequestStatus::SUCCESS
);
307 EXPECT_CALL(service
, EndFetchModel(
308 ClientSideDetectionService::MODEL_MISSING_FIELDS
))
309 .WillOnce(QuitCurrentMessageLoop());
310 service
.StartFetchModel();
311 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
312 Mock::VerifyAndClearExpectations(&service
);
314 // Model that points to hashes that don't exist.
315 model
.set_version(10);
316 model
.add_hashes("bla");
317 model
.add_page_term(1); // Should be 0 instead of 1.
318 SetModelFetchResponse(model
.SerializePartialAsString(), net::HTTP_OK
,
319 net::URLRequestStatus::SUCCESS
);
320 EXPECT_CALL(service
, EndFetchModel(
321 ClientSideDetectionService::MODEL_BAD_HASH_IDS
))
322 .WillOnce(QuitCurrentMessageLoop());
323 service
.StartFetchModel();
324 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
325 Mock::VerifyAndClearExpectations(&service
);
326 model
.set_page_term(0, 0);
328 // Model version number is wrong.
329 model
.set_version(-1);
330 SetModelFetchResponse(model
.SerializeAsString(), net::HTTP_OK
,
331 net::URLRequestStatus::SUCCESS
);
332 EXPECT_CALL(service
, EndFetchModel(
333 ClientSideDetectionService::MODEL_INVALID_VERSION_NUMBER
))
334 .WillOnce(QuitCurrentMessageLoop());
335 service
.StartFetchModel();
336 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
337 Mock::VerifyAndClearExpectations(&service
);
340 model
.set_version(10);
341 SetModelFetchResponse(model
.SerializeAsString(), net::HTTP_OK
,
342 net::URLRequestStatus::SUCCESS
);
343 EXPECT_CALL(service
, EndFetchModel(
344 ClientSideDetectionService::MODEL_SUCCESS
))
345 .WillOnce(QuitCurrentMessageLoop());
346 service
.StartFetchModel();
347 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
348 Mock::VerifyAndClearExpectations(&service
);
350 // Model version number is decreasing. Set the model version number of the
351 // model that is currently loaded in the service object to 11.
352 service
.model_
.reset(new ClientSideModel(model
));
353 service
.model_
->set_version(11);
354 SetModelFetchResponse(model
.SerializeAsString(), net::HTTP_OK
,
355 net::URLRequestStatus::SUCCESS
);
356 EXPECT_CALL(service
, EndFetchModel(
357 ClientSideDetectionService::MODEL_INVALID_VERSION_NUMBER
))
358 .WillOnce(QuitCurrentMessageLoop());
359 service
.StartFetchModel();
360 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
361 Mock::VerifyAndClearExpectations(&service
);
363 // Model version hasn't changed since the last reload.
364 service
.model_
->set_version(10);
365 SetModelFetchResponse(model
.SerializeAsString(), net::HTTP_OK
,
366 net::URLRequestStatus::SUCCESS
);
367 EXPECT_CALL(service
, EndFetchModel(
368 ClientSideDetectionService::MODEL_NOT_CHANGED
))
369 .WillOnce(QuitCurrentMessageLoop());
370 service
.StartFetchModel();
371 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
372 Mock::VerifyAndClearExpectations(&service
);
375 TEST_F(ClientSideDetectionServiceTest
, ServiceObjectDeletedBeforeCallbackDone
) {
376 SetModelFetchResponse("bogus model", net::HTTP_OK
,
377 net::URLRequestStatus::SUCCESS
);
378 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
379 csd_service_
->SetEnabledAndRefreshState(true);
380 EXPECT_TRUE(csd_service_
.get() != NULL
);
381 // We delete the client-side detection service class even though the callbacks
383 csd_service_
.reset();
384 // Waiting for the callbacks to run should not crash even if the service
386 msg_loop_
.RunUntilIdle();
389 TEST_F(ClientSideDetectionServiceTest
, SendClientReportPhishingRequest
) {
390 SetModelFetchResponse("bogus model", net::HTTP_OK
,
391 net::URLRequestStatus::SUCCESS
);
392 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
393 csd_service_
->SetEnabledAndRefreshState(true);
395 GURL
url("http://a.com/");
396 float score
= 0.4f
; // Some random client score.
398 base::Time before
= base::Time::Now();
400 // Invalid response body from the server.
401 SetClientReportPhishingResponse("invalid proto response", net::HTTP_OK
,
402 net::URLRequestStatus::SUCCESS
);
403 EXPECT_FALSE(SendClientReportPhishingRequest(url
, score
));
406 ClientPhishingResponse response
;
407 response
.set_phishy(true);
408 SetClientReportPhishingResponse(response
.SerializeAsString(), net::HTTP_OK
,
409 net::URLRequestStatus::SUCCESS
);
410 EXPECT_TRUE(SendClientReportPhishingRequest(url
, score
));
412 // This request will fail
413 GURL
second_url("http://b.com/");
414 response
.set_phishy(false);
415 SetClientReportPhishingResponse(response
.SerializeAsString(),
416 net::HTTP_INTERNAL_SERVER_ERROR
,
417 net::URLRequestStatus::FAILED
);
418 EXPECT_FALSE(SendClientReportPhishingRequest(second_url
, score
));
420 base::Time after
= base::Time::Now();
422 // Check that we have recorded all 3 requests within the correct time range.
423 std::queue
<base::Time
>& report_times
= GetPhishingReportTimes();
424 EXPECT_EQ(3U, report_times
.size());
425 while (!report_times
.empty()) {
426 base::Time time
= report_times
.back();
428 EXPECT_LE(before
, time
);
429 EXPECT_GE(after
, time
);
432 // Only the first url should be in the cache.
434 EXPECT_TRUE(csd_service_
->IsInCache(url
));
435 EXPECT_TRUE(csd_service_
->GetValidCachedResult(url
, &is_phishing
));
436 EXPECT_TRUE(is_phishing
);
437 EXPECT_FALSE(csd_service_
->IsInCache(second_url
));
440 TEST_F(ClientSideDetectionServiceTest
, SendClientReportMalwareRequest
) {
441 SetModelFetchResponse("bogus model", net::HTTP_OK
,
442 net::URLRequestStatus::SUCCESS
);
443 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
444 csd_service_
->SetEnabledAndRefreshState(true);
445 GURL
url("http://a.com/");
447 base::Time before
= base::Time::Now();
448 // Invalid response body from the server.
449 SetClientReportMalwareResponse("invalid proto response", net::HTTP_OK
,
450 net::URLRequestStatus::SUCCESS
);
451 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
454 ClientMalwareResponse response
;
455 response
.set_blacklist(true);
456 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
457 net::URLRequestStatus::SUCCESS
);
458 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
461 response
.set_blacklist(true);
462 response
.set_bad_url("http://response-bad.com/");
463 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
464 net::URLRequestStatus::SUCCESS
);
465 EXPECT_TRUE(SendClientReportMalwareRequest(url
));
466 CheckConfirmedMalwareUrl(GURL("http://response-bad.com/"));
468 // This request will fail
469 response
.set_blacklist(false);
470 SetClientReportMalwareResponse(response
.SerializeAsString(),
471 net::HTTP_INTERNAL_SERVER_ERROR
,
472 net::URLRequestStatus::FAILED
);
473 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
475 // server blacklist decision is false, and response is succesful
476 response
.set_blacklist(false);
477 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
478 net::URLRequestStatus::SUCCESS
);
479 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
481 // Check that we have recorded all 4 requests within the correct time range.
482 base::Time after
= base::Time::Now();
483 std::queue
<base::Time
>& report_times
= GetMalwareReportTimes();
484 EXPECT_EQ(4U, report_times
.size());
486 // Another normal behavior will fail because of the limit is hit
487 response
.set_blacklist(true);
488 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
489 net::URLRequestStatus::SUCCESS
);
490 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
492 report_times
= GetMalwareReportTimes();
493 EXPECT_EQ(4U, report_times
.size());
494 while (!report_times
.empty()) {
495 base::Time time
= report_times
.back();
497 EXPECT_LE(before
, time
);
498 EXPECT_GE(after
, time
);
502 TEST_F(ClientSideDetectionServiceTest
, GetNumReportTest
) {
503 SetModelFetchResponse("bogus model", net::HTTP_OK
,
504 net::URLRequestStatus::SUCCESS
);
505 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
507 std::queue
<base::Time
>& report_times
= GetPhishingReportTimes();
508 base::Time now
= base::Time::Now();
509 base::TimeDelta twenty_five_hours
= base::TimeDelta::FromHours(25);
510 report_times
.push(now
- twenty_five_hours
);
511 report_times
.push(now
- twenty_five_hours
);
512 report_times
.push(now
);
513 report_times
.push(now
);
515 EXPECT_EQ(2, GetNumReports(&report_times
));
518 TEST_F(ClientSideDetectionServiceTest
, CacheTest
) {
519 SetModelFetchResponse("bogus model", net::HTTP_OK
,
520 net::URLRequestStatus::SUCCESS
);
521 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
526 TEST_F(ClientSideDetectionServiceTest
, IsPrivateIPAddress
) {
527 SetModelFetchResponse("bogus model", net::HTTP_OK
,
528 net::URLRequestStatus::SUCCESS
);
529 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
531 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("10.1.2.3"));
532 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("127.0.0.1"));
533 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("172.24.3.4"));
534 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("192.168.1.1"));
535 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("fc00::"));
536 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("fec0::"));
537 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("fec0:1:2::3"));
538 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("::1"));
540 EXPECT_FALSE(csd_service_
->IsPrivateIPAddress("1.2.3.4"));
541 EXPECT_FALSE(csd_service_
->IsPrivateIPAddress("200.1.1.1"));
542 EXPECT_FALSE(csd_service_
->IsPrivateIPAddress("2001:0db8:ac10:fe01::"));
544 // If the address can't be parsed, the default is true.
545 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("blah"));
548 TEST_F(ClientSideDetectionServiceTest
, SetBadSubnets
) {
549 ClientSideModel model
;
550 ClientSideDetectionService::BadSubnetMap bad_subnets
;
551 ClientSideDetectionService::SetBadSubnets(model
, &bad_subnets
);
552 EXPECT_EQ(0U, bad_subnets
.size());
554 // Bad subnets are skipped.
555 ClientSideModel::IPSubnet
* subnet
= model
.add_bad_subnet();
556 subnet
->set_prefix(std::string(crypto::kSHA256Length
, '.'));
557 subnet
->set_size(130); // Invalid size.
559 subnet
= model
.add_bad_subnet();
560 subnet
->set_prefix(std::string(crypto::kSHA256Length
, '.'));
561 subnet
->set_size(-1); // Invalid size.
563 subnet
= model
.add_bad_subnet();
564 subnet
->set_prefix(std::string(16, '.')); // Invalid len.
565 subnet
->set_size(64);
567 ClientSideDetectionService::SetBadSubnets(model
, &bad_subnets
);
568 EXPECT_EQ(0U, bad_subnets
.size());
570 subnet
= model
.add_bad_subnet();
571 subnet
->set_prefix(std::string(crypto::kSHA256Length
, '.'));
572 subnet
->set_size(64);
574 subnet
= model
.add_bad_subnet();
575 subnet
->set_prefix(std::string(crypto::kSHA256Length
, ','));
576 subnet
->set_size(64);
578 subnet
= model
.add_bad_subnet();
579 subnet
->set_prefix(std::string(crypto::kSHA256Length
, '.'));
580 subnet
->set_size(128);
582 subnet
= model
.add_bad_subnet();
583 subnet
->set_prefix(std::string(crypto::kSHA256Length
, '.'));
584 subnet
->set_size(100);
586 ClientSideDetectionService::SetBadSubnets(model
, &bad_subnets
);
587 EXPECT_EQ(3U, bad_subnets
.size());
588 ClientSideDetectionService::BadSubnetMap::const_iterator it
;
589 std::string mask
= std::string(8, '\xFF') + std::string(8, '\x00');
590 EXPECT_TRUE(bad_subnets
.count(mask
));
591 EXPECT_TRUE(bad_subnets
[mask
].count(std::string(crypto::kSHA256Length
, '.')));
592 EXPECT_TRUE(bad_subnets
[mask
].count(std::string(crypto::kSHA256Length
, ',')));
594 mask
= std::string(16, '\xFF');
595 EXPECT_TRUE(bad_subnets
.count(mask
));
596 EXPECT_TRUE(bad_subnets
[mask
].count(std::string(crypto::kSHA256Length
, '.')));
598 mask
= std::string(12, '\xFF') + "\xF0" + std::string(3, '\x00');
599 EXPECT_TRUE(bad_subnets
.count(mask
));
600 EXPECT_TRUE(bad_subnets
[mask
].count(std::string(crypto::kSHA256Length
, '.')));
603 TEST_F(ClientSideDetectionServiceTest
, ModelHasValidHashIds
) {
604 ClientSideModel model
;
605 EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model
));
606 model
.add_hashes("bla");
607 EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model
));
608 model
.add_page_term(0);
609 EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model
));
611 model
.add_page_term(-1);
612 EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model
));
613 model
.set_page_term(1, 1);
614 EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model
));
615 model
.set_page_term(1, 0);
616 EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model
));
619 model
.add_hashes("blu");
620 ClientSideModel::Rule
* rule
= model
.add_rule();
621 rule
->add_feature(0);
622 rule
->add_feature(1);
623 rule
->set_weight(0.1f
);
624 EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model
));
626 rule
= model
.add_rule();
627 rule
->add_feature(0);
628 rule
->add_feature(1);
629 rule
->add_feature(-1);
630 rule
->set_weight(0.2f
);
631 EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model
));
633 rule
->set_feature(2, 2);
634 EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model
));
636 rule
->set_feature(2, 1);
637 EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model
));
640 TEST_F(ClientSideDetectionServiceTest
, SetEnabledAndRefreshState
) {
641 // Check that the model isn't downloaded until the service is enabled.
642 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
643 EXPECT_FALSE(csd_service_
->enabled());
644 EXPECT_TRUE(csd_service_
->model_fetcher_
.get() == NULL
);
646 // Use a MockClientSideDetectionService for the rest of the test, to avoid
647 // the scheduling delay.
648 MockClientSideDetectionService
* service
=
649 new StrictMock
<MockClientSideDetectionService
>();
650 csd_service_
.reset(service
);
651 EXPECT_FALSE(csd_service_
->enabled());
652 EXPECT_TRUE(csd_service_
->model_fetcher_
.get() == NULL
);
653 // No calls expected yet.
654 Mock::VerifyAndClearExpectations(service
);
656 ClientSideModel model
;
657 model
.set_version(10);
658 model
.set_max_words_per_term(4);
659 SetModelFetchResponse(model
.SerializeAsString(), net::HTTP_OK
,
660 net::URLRequestStatus::SUCCESS
);
661 EXPECT_CALL(*service
, ScheduleFetchModel(_
))
662 .WillOnce(Invoke(service
, &MockClientSideDetectionService::Schedule
));
663 EXPECT_CALL(*service
, EndFetchModel(
664 ClientSideDetectionService::MODEL_SUCCESS
))
665 .WillOnce(QuitCurrentMessageLoop());
666 csd_service_
->SetEnabledAndRefreshState(true);
667 EXPECT_TRUE(csd_service_
->model_fetcher_
.get() != NULL
);
668 msg_loop_
.Run(); // EndFetchModel will quit the message loop.
669 Mock::VerifyAndClearExpectations(service
);
671 // Check that enabling again doesn't request the model.
672 csd_service_
->SetEnabledAndRefreshState(true);
673 // No calls expected.
674 Mock::VerifyAndClearExpectations(service
);
676 // Check that disabling the service cancels pending requests.
677 EXPECT_CALL(*service
, ScheduleFetchModel(_
))
678 .WillOnce(Invoke(service
, &MockClientSideDetectionService::Schedule
));
679 csd_service_
->SetEnabledAndRefreshState(false);
680 csd_service_
->SetEnabledAndRefreshState(true);
681 Mock::VerifyAndClearExpectations(service
);
682 EXPECT_TRUE(csd_service_
->model_fetcher_
.get() != NULL
);
683 csd_service_
->SetEnabledAndRefreshState(false);
684 EXPECT_TRUE(csd_service_
->model_fetcher_
.get() == NULL
);
685 msg_loop_
.RunUntilIdle();
686 // No calls expected.
687 Mock::VerifyAndClearExpectations(service
);
689 // Requests always return false when the service is disabled.
690 ClientPhishingResponse response
;
691 response
.set_phishy(true);
692 SetClientReportPhishingResponse(response
.SerializeAsString(), net::HTTP_OK
,
693 net::URLRequestStatus::SUCCESS
);
694 EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f
));
696 // Pending requests also return false if the service is disabled before they
698 EXPECT_CALL(*service
, ScheduleFetchModel(_
))
699 .WillOnce(Invoke(service
, &MockClientSideDetectionService::Schedule
));
700 EXPECT_CALL(*service
, EndFetchModel(
701 ClientSideDetectionService::MODEL_NOT_CHANGED
))
702 .WillOnce(Invoke(service
, &MockClientSideDetectionService::Disable
));
703 csd_service_
->SetEnabledAndRefreshState(true);
704 EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f
));
705 Mock::VerifyAndClearExpectations(service
);
707 } // namespace safe_browsing