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 <map>
6#include <queue>
7#include <string>
8
9#include "base/bind.h"
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"
25#include "url/gurl.h"
26
27using ::testing::Invoke;
28using ::testing::Mock;
29using ::testing::StrictMock;
30using ::testing::_;
31using content::BrowserThread;
32
33namespace safe_browsing {
34namespace {
35class MockClientSideDetectionService : public ClientSideDetectionService {
36 public:
37  MockClientSideDetectionService() : ClientSideDetectionService(NULL) {}
38  virtual ~MockClientSideDetectionService() {}
39
40  MOCK_METHOD1(EndFetchModel, void(ClientModelStatus));
41  MOCK_METHOD1(ScheduleFetchModel, void(int64));
42
43  void Schedule(int64) {
44    // Ignore the delay when testing.
45    StartFetchModel();
46  }
47
48  void Disable(int) {
49    // Ignore the status.
50    SetEnabledAndRefreshState(false);
51  }
52
53 private:
54  DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
55};
56
57ACTION(QuitCurrentMessageLoop) {
58  base::MessageLoop::current()->Quit();
59}
60
61}  // namespace
62
63class ClientSideDetectionServiceTest : public testing::Test {
64 protected:
65  virtual void SetUp() {
66    file_thread_.reset(new content::TestBrowserThread(BrowserThread::FILE,
67                                                      &msg_loop_));
68
69    factory_.reset(new net::FakeURLFetcherFactory(NULL));
70
71    browser_thread_.reset(new content::TestBrowserThread(BrowserThread::UI,
72                                                         &msg_loop_));
73  }
74
75  virtual void TearDown() {
76    msg_loop_.RunUntilIdle();
77    csd_service_.reset();
78    file_thread_.reset();
79    browser_thread_.reset();
80  }
81
82  bool SendClientReportPhishingRequest(const GURL& phishing_url,
83                                       float score) {
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(
89        request,
90        base::Bind(&ClientSideDetectionServiceTest::SendRequestDone,
91                   base::Unretained(this)));
92    phishing_url_ = phishing_url;
93    msg_loop_.Run();  // Waits until callback is called.
94    return is_phishing_;
95  }
96
97  bool SendClientReportMalwareRequest(const GURL& url) {
98    scoped_ptr<ClientMalwareRequest> request(new ClientMalwareRequest());
99    request->set_url(url.spec());
100    csd_service_->SendClientReportMalwareRequest(
101        request.release(),
102        base::Bind(&ClientSideDetectionServiceTest::SendMalwareRequestDone,
103                   base::Unretained(this)));
104    phishing_url_ = url;
105    msg_loop_.Run();  // Waits until callback is called.
106    return is_malware_;
107  }
108
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);
114  }
115
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);
123  }
124
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);
132  }
133
134  int GetNumReports(std::queue<base::Time>* report_times) {
135    return csd_service_->GetNumReports(report_times);
136  }
137
138  std::queue<base::Time>& GetPhishingReportTimes() {
139    return csd_service_->phishing_report_times_;
140  }
141
142  std::queue<base::Time>& GetMalwareReportTimes() {
143    return csd_service_->malware_report_times_;
144  }
145
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,
149                                                                   time));
150  }
151
152  void TestCache() {
153    ClientSideDetectionService::PhishingCache& cache = csd_service_->cache_;
154    base::Time now = base::Time::Now();
155    base::Time time =
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,
161                                                                   time));
162
163    time =
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,
169                                                                   time));
170
171    time =
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));
177
178    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));
184
185    csd_service_->UpdateCache();
186
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());
192
193    // While 3 elements remain, only the first and the fourth are actually
194    // valid.
195    bool is_phishing;
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);
204  }
205
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);
211  }
212
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);
219  }
220
221  void CheckConfirmedMalwareUrl(GURL url) {
222    ASSERT_EQ(confirmed_malware_url_, url);
223  }
224
225 protected:
226  scoped_ptr<ClientSideDetectionService> csd_service_;
227  scoped_ptr<net::FakeURLFetcherFactory> factory_;
228  base::MessageLoop msg_loop_;
229
230 private:
231  void SendRequestDone(GURL phishing_url, bool is_phishing) {
232    ASSERT_EQ(phishing_url, phishing_url_);
233    is_phishing_ = is_phishing;
234    msg_loop_.Quit();
235  }
236
237  void SendMalwareRequestDone(GURL original_url, GURL malware_url,
238                              bool is_malware) {
239    ASSERT_EQ(phishing_url_, original_url);
240    confirmed_malware_url_ = malware_url;
241    is_malware_ = is_malware;
242    msg_loop_.Quit();
243  }
244
245  scoped_ptr<content::TestBrowserThread> browser_thread_;
246  scoped_ptr<content::TestBrowserThread> file_thread_;
247
248  GURL phishing_url_;
249  GURL confirmed_malware_url_;
250  bool is_phishing_;
251  bool is_malware_;
252};
253
254TEST_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);
261
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);
271
272  // Empty model file.
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);
280
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);
291
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);
301
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);
313
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);
327
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);
338
339  // Normal model.
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);
349
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);
362
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);
373}
374
375TEST_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
382  // haven't run yet.
383  csd_service_.reset();
384  // Waiting for the callbacks to run should not crash even if the service
385  // object is gone.
386  msg_loop_.RunUntilIdle();
387}
388
389TEST_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);
394
395  GURL url("http://a.com/");
396  float score = 0.4f;  // Some random client score.
397
398  base::Time before = base::Time::Now();
399
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));
404
405  // Normal behavior.
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));
411
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));
419
420  base::Time after = base::Time::Now();
421
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();
427    report_times.pop();
428    EXPECT_LE(before, time);
429    EXPECT_GE(after, time);
430  }
431
432  // Only the first url should be in the cache.
433  bool is_phishing;
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));
438}
439
440TEST_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/");
446
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));
452
453  // Missing bad_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));
459
460  // Normal behavior.
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/"));
467
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));
474
475  // Server blacklist decision is false, and response is successful
476  response.set_blacklist(false);
477  SetClientReportMalwareResponse(response.SerializeAsString(), net::HTTP_OK,
478                                 net::URLRequestStatus::SUCCESS);
479  EXPECT_FALSE(SendClientReportMalwareRequest(url));
480
481  // Check that we have recorded all 5 requests within the correct time range.
482  base::Time after = base::Time::Now();
483  std::queue<base::Time>& report_times = GetMalwareReportTimes();
484  EXPECT_EQ(5U, report_times.size());
485
486  // Check that the malware report limit was reached.
487  EXPECT_TRUE(csd_service_->OverMalwareReportLimit());
488
489  report_times = GetMalwareReportTimes();
490  EXPECT_EQ(5U, report_times.size());
491  while (!report_times.empty()) {
492    base::Time time = report_times.back();
493    report_times.pop();
494    EXPECT_LE(before, time);
495    EXPECT_GE(after, time);
496  }
497}
498
499TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) {
500  SetModelFetchResponse("bogus model", net::HTTP_OK,
501                        net::URLRequestStatus::SUCCESS);
502  csd_service_.reset(ClientSideDetectionService::Create(NULL));
503
504  std::queue<base::Time>& report_times = GetPhishingReportTimes();
505  base::Time now = base::Time::Now();
506  base::TimeDelta twenty_five_hours = base::TimeDelta::FromHours(25);
507  report_times.push(now - twenty_five_hours);
508  report_times.push(now - twenty_five_hours);
509  report_times.push(now);
510  report_times.push(now);
511
512  EXPECT_EQ(2, GetNumReports(&report_times));
513}
514
515TEST_F(ClientSideDetectionServiceTest, CacheTest) {
516  SetModelFetchResponse("bogus model", net::HTTP_OK,
517                        net::URLRequestStatus::SUCCESS);
518  csd_service_.reset(ClientSideDetectionService::Create(NULL));
519
520  TestCache();
521}
522
523TEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
524  SetModelFetchResponse("bogus model", net::HTTP_OK,
525                        net::URLRequestStatus::SUCCESS);
526  csd_service_.reset(ClientSideDetectionService::Create(NULL));
527
528  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3"));
529  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("127.0.0.1"));
530  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("172.24.3.4"));
531  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("192.168.1.1"));
532  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fc00::"));
533  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0::"));
534  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0:1:2::3"));
535  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("::1"));
536
537  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("1.2.3.4"));
538  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("200.1.1.1"));
539  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("2001:0db8:ac10:fe01::"));
540
541  // If the address can't be parsed, the default is true.
542  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("blah"));
543}
544
545TEST_F(ClientSideDetectionServiceTest, SetBadSubnets) {
546  ClientSideModel model;
547  ClientSideDetectionService::BadSubnetMap bad_subnets;
548  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
549  EXPECT_EQ(0U, bad_subnets.size());
550
551  // Bad subnets are skipped.
552  ClientSideModel::IPSubnet* subnet = model.add_bad_subnet();
553  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
554  subnet->set_size(130);  // Invalid size.
555
556  subnet = model.add_bad_subnet();
557  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
558  subnet->set_size(-1);  // Invalid size.
559
560  subnet = model.add_bad_subnet();
561  subnet->set_prefix(std::string(16, '.'));  // Invalid len.
562  subnet->set_size(64);
563
564  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
565  EXPECT_EQ(0U, bad_subnets.size());
566
567  subnet = model.add_bad_subnet();
568  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
569  subnet->set_size(64);
570
571  subnet = model.add_bad_subnet();
572  subnet->set_prefix(std::string(crypto::kSHA256Length, ','));
573  subnet->set_size(64);
574
575  subnet = model.add_bad_subnet();
576  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
577  subnet->set_size(128);
578
579  subnet = model.add_bad_subnet();
580  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
581  subnet->set_size(100);
582
583  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
584  EXPECT_EQ(3U, bad_subnets.size());
585  ClientSideDetectionService::BadSubnetMap::const_iterator it;
586  std::string mask = std::string(8, '\xFF') + std::string(8, '\x00');
587  EXPECT_TRUE(bad_subnets.count(mask));
588  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
589  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, ',')));
590
591  mask = std::string(16, '\xFF');
592  EXPECT_TRUE(bad_subnets.count(mask));
593  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
594
595  mask = std::string(12, '\xFF') + "\xF0" + std::string(3, '\x00');
596  EXPECT_TRUE(bad_subnets.count(mask));
597  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
598}
599
600TEST_F(ClientSideDetectionServiceTest, ModelHasValidHashIds) {
601  ClientSideModel model;
602  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
603  model.add_hashes("bla");
604  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
605  model.add_page_term(0);
606  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
607
608  model.add_page_term(-1);
609  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
610  model.set_page_term(1, 1);
611  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
612  model.set_page_term(1, 0);
613  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
614
615  // Test bad rules.
616  model.add_hashes("blu");
617  ClientSideModel::Rule* rule = model.add_rule();
618  rule->add_feature(0);
619  rule->add_feature(1);
620  rule->set_weight(0.1f);
621  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
622
623  rule = model.add_rule();
624  rule->add_feature(0);
625  rule->add_feature(1);
626  rule->add_feature(-1);
627  rule->set_weight(0.2f);
628  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
629
630  rule->set_feature(2, 2);
631  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
632
633  rule->set_feature(2, 1);
634  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
635}
636
637TEST_F(ClientSideDetectionServiceTest, SetEnabledAndRefreshState) {
638  // Check that the model isn't downloaded until the service is enabled.
639  csd_service_.reset(ClientSideDetectionService::Create(NULL));
640  EXPECT_FALSE(csd_service_->enabled());
641  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
642
643  // Use a MockClientSideDetectionService for the rest of the test, to avoid
644  // the scheduling delay.
645  MockClientSideDetectionService* service =
646      new StrictMock<MockClientSideDetectionService>();
647  csd_service_.reset(service);
648  EXPECT_FALSE(csd_service_->enabled());
649  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
650  // No calls expected yet.
651  Mock::VerifyAndClearExpectations(service);
652
653  ClientSideModel model;
654  model.set_version(10);
655  model.set_max_words_per_term(4);
656  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
657                        net::URLRequestStatus::SUCCESS);
658  EXPECT_CALL(*service, ScheduleFetchModel(_))
659      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
660  EXPECT_CALL(*service, EndFetchModel(
661      ClientSideDetectionService::MODEL_SUCCESS))
662      .WillOnce(QuitCurrentMessageLoop());
663  csd_service_->SetEnabledAndRefreshState(true);
664  EXPECT_TRUE(csd_service_->model_fetcher_.get() != NULL);
665  msg_loop_.Run();  // EndFetchModel will quit the message loop.
666  Mock::VerifyAndClearExpectations(service);
667
668  // Check that enabling again doesn't request the model.
669  csd_service_->SetEnabledAndRefreshState(true);
670  // No calls expected.
671  Mock::VerifyAndClearExpectations(service);
672
673  // Check that disabling the service cancels pending requests.
674  EXPECT_CALL(*service, ScheduleFetchModel(_))
675      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
676  csd_service_->SetEnabledAndRefreshState(false);
677  csd_service_->SetEnabledAndRefreshState(true);
678  Mock::VerifyAndClearExpectations(service);
679  EXPECT_TRUE(csd_service_->model_fetcher_.get() != NULL);
680  csd_service_->SetEnabledAndRefreshState(false);
681  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
682  msg_loop_.RunUntilIdle();
683  // No calls expected.
684  Mock::VerifyAndClearExpectations(service);
685
686  // Requests always return false when the service is disabled.
687  ClientPhishingResponse response;
688  response.set_phishy(true);
689  SetClientReportPhishingResponse(response.SerializeAsString(), net::HTTP_OK,
690                                  net::URLRequestStatus::SUCCESS);
691  EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f));
692
693  // Pending requests also return false if the service is disabled before they
694  // report back.
695  EXPECT_CALL(*service, ScheduleFetchModel(_))
696      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
697  EXPECT_CALL(*service, EndFetchModel(
698      ClientSideDetectionService::MODEL_NOT_CHANGED))
699      .WillOnce(Invoke(service, &MockClientSideDetectionService::Disable));
700  csd_service_->SetEnabledAndRefreshState(true);
701  EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f));
702  Mock::VerifyAndClearExpectations(service);
703}
704}  // namespace safe_browsing
705