15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <map>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <queue>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
139ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/safe_browsing/client_side_detection_service.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/safe_browsing/client_model.pb.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/safe_browsing/csd.pb.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/test/test_browser_thread.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/sha2.h"
200f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)#include "net/http/http_status_code.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/test_url_fetcher_factory.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_status.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gmock/include/gmock/gmock.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "url/gurl.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ::testing::Invoke;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ::testing::Mock;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ::testing::StrictMock;
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)using ::testing::_;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace safe_browsing {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockClientSideDetectionService : public ClientSideDetectionService {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockClientSideDetectionService() : ClientSideDetectionService(NULL) {}
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~MockClientSideDetectionService() {}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MOCK_METHOD1(EndFetchModel, void(ClientModelStatus));
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MOCK_METHOD1(ScheduleFetchModel, void(int64));
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Schedule(int64) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Ignore the delay when testing.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartFetchModel();
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void Disable(int) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Ignore the status.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetEnabledAndRefreshState(false);
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ACTION(QuitCurrentMessageLoop) {
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop::current()->Quit();
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ClientSideDetectionServiceTest : public testing::Test {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SetUp() {
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_thread_.reset(new content::TestBrowserThread(BrowserThread::FILE,
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                      &msg_loop_));
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    factory_.reset(new net::FakeURLFetcherFactory(NULL));
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_thread_.reset(new content::TestBrowserThread(BrowserThread::UI,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                         &msg_loop_));
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void TearDown() {
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    msg_loop_.RunUntilIdle();
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    csd_service_.reset();
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_thread_.reset();
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    browser_thread_.reset();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool SendClientReportPhishingRequest(const GURL& phishing_url,
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       float score) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ClientPhishingRequest* request = new ClientPhishingRequest();
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request->set_url(phishing_url.spec());
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request->set_client_score(score);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request->set_is_phishing(true);  // client thinks the URL is phishing.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    csd_service_->SendClientReportPhishingRequest(
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        request,
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::Bind(&ClientSideDetectionServiceTest::SendRequestDone,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   base::Unretained(this)));
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    phishing_url_ = phishing_url;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    msg_loop_.Run();  // Waits until callback is called.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return is_phishing_;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool SendClientReportMalwareRequest(const GURL& url) {
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<ClientMalwareRequest> request(new ClientMalwareRequest());
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    request->set_url(url.spec());
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    csd_service_->SendClientReportMalwareRequest(
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        request.release(),
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&ClientSideDetectionServiceTest::SendMalwareRequestDone,
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   base::Unretained(this)));
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    phishing_url_ = url;
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    msg_loop_.Run();  // Waits until callback is called.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return is_malware_;
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1090f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  void SetModelFetchResponse(std::string response_data,
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             net::HttpStatusCode response_code,
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             net::URLRequestStatus::Status status) {
1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    factory_->SetFakeResponse(GURL(ClientSideDetectionService::kClientModelUrl),
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                              response_data, response_code, status);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetClientReportPhishingResponse(std::string response_data,
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                       net::HttpStatusCode response_code,
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                       net::URLRequestStatus::Status status) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    factory_->SetFakeResponse(
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ClientSideDetectionService::GetClientReportUrl(
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            ClientSideDetectionService::kClientReportPhishingUrl),
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        response_data, response_code, status);
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void SetClientReportMalwareResponse(std::string response_data,
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                      net::HttpStatusCode response_code,
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                      net::URLRequestStatus::Status status) {
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    factory_->SetFakeResponse(
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ClientSideDetectionService::GetClientReportUrl(
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            ClientSideDetectionService::kClientReportMalwareUrl),
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        response_data, response_code, status);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  int GetNumReports(std::queue<base::Time>* report_times) {
13590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return csd_service_->GetNumReports(report_times);
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::queue<base::Time>& GetPhishingReportTimes() {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return csd_service_->phishing_report_times_;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::queue<base::Time>& GetMalwareReportTimes() {
14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return csd_service_->malware_report_times_;
14490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetCache(const GURL& gurl, bool is_phishing, base::Time time) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    csd_service_->cache_[gurl] =
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        make_linked_ptr(new ClientSideDetectionService::CacheState(is_phishing,
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                                   time));
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void TestCache() {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ClientSideDetectionService::PhishingCache& cache = csd_service_->cache_;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Time now = base::Time::Now();
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Time time =
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        now - base::TimeDelta::FromDays(
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ClientSideDetectionService::kNegativeCacheIntervalDays) +
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMinutes(5);
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cache[GURL("http://first.url.com/")] =
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        make_linked_ptr(new ClientSideDetectionService::CacheState(false,
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                                   time));
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time =
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        now - base::TimeDelta::FromDays(
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ClientSideDetectionService::kNegativeCacheIntervalDays) -
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromHours(1);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cache[GURL("http://second.url.com/")] =
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        make_linked_ptr(new ClientSideDetectionService::CacheState(false,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                                   time));
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time =
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        now - base::TimeDelta::FromMinutes(
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ClientSideDetectionService::kPositiveCacheIntervalMinutes) -
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMinutes(5);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cache[GURL("http://third.url.com/")] =
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        make_linked_ptr(new ClientSideDetectionService::CacheState(true, time));
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time =
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        now - base::TimeDelta::FromMinutes(
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            ClientSideDetectionService::kPositiveCacheIntervalMinutes) +
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::TimeDelta::FromMinutes(5);
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    cache[GURL("http://fourth.url.com/")] =
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        make_linked_ptr(new ClientSideDetectionService::CacheState(true, time));
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    csd_service_->UpdateCache();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // 3 elements should be in the cache, the first, third, and fourth.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_EQ(3U, cache.size());
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(cache.find(GURL("http://first.url.com/")) != cache.end());
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(cache.find(GURL("http://third.url.com/")) != cache.end());
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(cache.find(GURL("http://fourth.url.com/")) != cache.end());
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // While 3 elements remain, only the first and the fourth are actually
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // valid.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool is_phishing;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(csd_service_->GetValidCachedResult(
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GURL("http://first.url.com"), &is_phishing));
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_FALSE(is_phishing);
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_FALSE(csd_service_->GetValidCachedResult(
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GURL("http://third.url.com"), &is_phishing));
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(csd_service_->GetValidCachedResult(
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GURL("http://fourth.url.com"), &is_phishing));
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(is_phishing);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddFeature(const std::string& name, double value,
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  ClientPhishingRequest* request) {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ClientPhishingRequest_Feature* feature = request->add_feature_map();
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    feature->set_name(name);
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    feature->set_value(value);
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddNonModelFeature(const std::string& name, double value,
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          ClientPhishingRequest* request) {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ClientPhishingRequest_Feature* feature =
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        request->add_non_model_feature_map();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    feature->set_name(name);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    feature->set_value(value);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
221424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void CheckConfirmedMalwareUrl(GURL url) {
222424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    ASSERT_EQ(confirmed_malware_url_, url);
223424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
224424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<ClientSideDetectionService> csd_service_;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<net::FakeURLFetcherFactory> factory_;
22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::MessageLoop msg_loop_;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SendRequestDone(GURL phishing_url, bool is_phishing) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_EQ(phishing_url, phishing_url_);
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    is_phishing_ = is_phishing;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    msg_loop_.Quit();
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
237424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  void SendMalwareRequestDone(GURL original_url, GURL malware_url,
238424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)                              bool is_malware) {
239424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    ASSERT_EQ(phishing_url_, original_url);
240424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    confirmed_malware_url_ = malware_url;
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    is_malware_ = is_malware;
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    msg_loop_.Quit();
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
244424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<content::TestBrowserThread> browser_thread_;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<content::TestBrowserThread> file_thread_;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL phishing_url_;
249424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  GURL confirmed_malware_url_;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_phishing_;
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_malware_;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, FetchModelTest) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't want to use a real service class here because we can't call
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the real EndFetchModel.  It would reschedule a reload which might
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // make the test flaky.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockClientSideDetectionService service;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, ScheduleFetchModel(_)).Times(1);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.SetEnabledAndRefreshState(true);
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The model fetch failed.
263f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("blamodel", net::HTTP_INTERNAL_SERVER_ERROR,
264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::FAILED);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_FETCH_FAILED))
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Empty model file.
273f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(std::string(), net::HTTP_OK,
274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(ClientSideDetectionService::MODEL_EMPTY))
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Model is too large.
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetModelFetchResponse(
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string(ClientSideDetectionService::kMaxModelSizeBytes + 1, 'x'),
284f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      net::HTTP_OK, net::URLRequestStatus::SUCCESS);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_TOO_LARGE))
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Unable to parse the model file.
293f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("Invalid model file", net::HTTP_OK,
294f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_PARSE_ERROR))
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Model that is missing some required fields (missing the version field).
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideModel model;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_max_words_per_term(4);
305f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializePartialAsString(), net::HTTP_OK,
306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_MISSING_FIELDS))
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Model that points to hashes that don't exist.
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_version(10);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.add_hashes("bla");
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.add_page_term(1);  // Should be 0 instead of 1.
318f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializePartialAsString(), net::HTTP_OK,
319f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_BAD_HASH_IDS))
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_page_term(0, 0);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Model version number is wrong.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_version(-1);
330f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
331f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_INVALID_VERSION_NUMBER))
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Normal model.
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_version(10);
341f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
342f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_SUCCESS))
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Model version number is decreasing.  Set the model version number of the
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // model that is currently loaded in the service object to 11.
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.model_.reset(new ClientSideModel(model));
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.model_->set_version(11);
354f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
355f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_INVALID_VERSION_NUMBER))
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Model version hasn't changed since the last reload.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.model_->set_version(10);
365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
366f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(service, EndFetchModel(
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_NOT_CHANGED))
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  service.StartFetchModel();
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(&service);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) {
376f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("bogus model", net::HTTP_OK,
377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_.get() != NULL);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We delete the client-side detection service class even though the callbacks
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // haven't run yet.
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset();
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Waiting for the callbacks to run should not crash even if the service
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // object is gone.
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  msg_loop_.RunUntilIdle();
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) {
390f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("bogus model", net::HTTP_OK,
391f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL url("http://a.com/");
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float score = 0.4f;  // Some random client score.
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time before = base::Time::Now();
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Invalid response body from the server.
401f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportPhishingResponse("invalid proto response", net::HTTP_OK,
402f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  net::URLRequestStatus::SUCCESS);
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(SendClientReportPhishingRequest(url, score));
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Normal behavior.
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientPhishingResponse response;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response.set_phishy(true);
408f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportPhishingResponse(response.SerializeAsString(), net::HTTP_OK,
409f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  net::URLRequestStatus::SUCCESS);
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(SendClientReportPhishingRequest(url, score));
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This request will fail
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL second_url("http://b.com/");
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response.set_phishy(false);
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetClientReportPhishingResponse(response.SerializeAsString(),
416f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  net::HTTP_INTERNAL_SERVER_ERROR,
417f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  net::URLRequestStatus::FAILED);
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(SendClientReportPhishingRequest(second_url, score));
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time after = base::Time::Now();
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that we have recorded all 3 requests within the correct time range.
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::queue<base::Time>& report_times = GetPhishingReportTimes();
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(3U, report_times.size());
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (!report_times.empty()) {
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::Time time = report_times.back();
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    report_times.pop();
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_LE(before, time);
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_GE(after, time);
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only the first url should be in the cache.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_phishing;
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsInCache(url));
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->GetValidCachedResult(url, &is_phishing));
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(is_phishing);
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(csd_service_->IsInCache(second_url));
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, SendClientReportMalwareRequest) {
441f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("bogus model", net::HTTP_OK,
442f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL url("http://a.com/");
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
44790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::Time before = base::Time::Now();
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Invalid response body from the server.
449f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportMalwareResponse("invalid proto response", net::HTTP_OK,
450f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::URLRequestStatus::SUCCESS);
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_FALSE(SendClientReportMalwareRequest(url));
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
453424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Missing bad_url.
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ClientMalwareResponse response;
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  response.set_blacklist(true);
456f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportMalwareResponse(response.SerializeAsString(), net::HTTP_OK,
457f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::URLRequestStatus::SUCCESS);
458424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  EXPECT_FALSE(SendClientReportMalwareRequest(url));
459424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
460424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // Normal behavior.
461424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  response.set_blacklist(true);
462424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  response.set_bad_url("http://response-bad.com/");
463f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportMalwareResponse(response.SerializeAsString(), net::HTTP_OK,
464f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::URLRequestStatus::SUCCESS);
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_TRUE(SendClientReportMalwareRequest(url));
466424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  CheckConfirmedMalwareUrl(GURL("http://response-bad.com/"));
4672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This request will fail
4692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  response.set_blacklist(false);
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SetClientReportMalwareResponse(response.SerializeAsString(),
471f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::HTTP_INTERNAL_SERVER_ERROR,
472f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::URLRequestStatus::FAILED);
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_FALSE(SendClientReportMalwareRequest(url));
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
475effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Server blacklist decision is false, and response is successful
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  response.set_blacklist(false);
477f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportMalwareResponse(response.SerializeAsString(), net::HTTP_OK,
478f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                 net::URLRequestStatus::SUCCESS);
4792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_FALSE(SendClientReportMalwareRequest(url));
48090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
481effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Check that we have recorded all 5 requests within the correct time range.
48290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::Time after = base::Time::Now();
48390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::queue<base::Time>& report_times = GetMalwareReportTimes();
484effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  EXPECT_EQ(5U, report_times.size());
48590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
486effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Check that the malware report limit was reached.
487effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  EXPECT_TRUE(csd_service_->OverMalwareReportLimit());
48890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
48990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  report_times = GetMalwareReportTimes();
490effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  EXPECT_EQ(5U, report_times.size());
49190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  while (!report_times.empty()) {
49290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    base::Time time = report_times.back();
49390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    report_times.pop();
49490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    EXPECT_LE(before, time);
49590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    EXPECT_GE(after, time);
49690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) {
500f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("bogus model", net::HTTP_OK,
501f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::queue<base::Time>& report_times = GetPhishingReportTimes();
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Time now = base::Time::Now();
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::TimeDelta twenty_five_hours = base::TimeDelta::FromHours(25);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  report_times.push(now - twenty_five_hours);
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  report_times.push(now - twenty_five_hours);
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  report_times.push(now);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  report_times.push(now);
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  EXPECT_EQ(2, GetNumReports(&report_times));
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, CacheTest) {
516f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("bogus model", net::HTTP_OK,
517f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TestCache();
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
524f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse("bogus model", net::HTTP_OK,
525f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3"));
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("127.0.0.1"));
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("172.24.3.4"));
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("192.168.1.1"));
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fc00::"));
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0::"));
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0:1:2::3"));
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("::1"));
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("1.2.3.4"));
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("200.1.1.1"));
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("2001:0db8:ac10:fe01::"));
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the address can't be parsed, the default is true.
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("blah"));
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, SetBadSubnets) {
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideModel model;
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideDetectionService::BadSubnetMap bad_subnets;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(0U, bad_subnets.size());
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Bad subnets are skipped.
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideModel::IPSubnet* subnet = model.add_bad_subnet();
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(130);  // Invalid size.
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet = model.add_bad_subnet();
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(-1);  // Invalid size.
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet = model.add_bad_subnet();
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(16, '.'));  // Invalid len.
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(64);
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(0U, bad_subnets.size());
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet = model.add_bad_subnet();
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(64);
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet = model.add_bad_subnet();
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(crypto::kSHA256Length, ','));
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(64);
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet = model.add_bad_subnet();
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(128);
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet = model.add_bad_subnet();
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_prefix(std::string(crypto::kSHA256Length, '.'));
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subnet->set_size(100);
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideDetectionService::SetBadSubnets(model, &bad_subnets);
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(3U, bad_subnets.size());
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideDetectionService::BadSubnetMap::const_iterator it;
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string mask = std::string(8, '\xFF') + std::string(8, '\x00');
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets.count(mask));
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, ',')));
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mask = std::string(16, '\xFF');
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets.count(mask));
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mask = std::string(12, '\xFF') + "\xF0" + std::string(3, '\x00');
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets.count(mask));
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(bad_subnets[mask].count(std::string(crypto::kSHA256Length, '.')));
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, ModelHasValidHashIds) {
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideModel model;
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.add_hashes("bla");
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.add_page_term(0);
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.add_page_term(-1);
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_page_term(1, 1);
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_page_term(1, 0);
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Test bad rules.
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.add_hashes("blu");
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideModel::Rule* rule = model.add_rule();
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->add_feature(0);
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->add_feature(1);
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->set_weight(0.1f);
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule = model.add_rule();
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->add_feature(0);
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->add_feature(1);
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->add_feature(-1);
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->set_weight(0.2f);
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->set_feature(2, 2);
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(ClientSideDetectionService::ModelHasValidHashIds(model));
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rule->set_feature(2, 1);
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(ClientSideDetectionService::ModelHasValidHashIds(model));
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(ClientSideDetectionServiceTest, SetEnabledAndRefreshState) {
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that the model isn't downloaded until the service is enabled.
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(ClientSideDetectionService::Create(NULL));
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(csd_service_->enabled());
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use a MockClientSideDetectionService for the rest of the test, to avoid
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the scheduling delay.
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockClientSideDetectionService* service =
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new StrictMock<MockClientSideDetectionService>();
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_.reset(service);
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(csd_service_->enabled());
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No calls expected yet.
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(service);
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientSideModel model;
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_version(10);
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  model.set_max_words_per_term(4);
656f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetModelFetchResponse(model.SerializeAsString(), net::HTTP_OK,
657f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        net::URLRequestStatus::SUCCESS);
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(*service, ScheduleFetchModel(_))
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(*service, EndFetchModel(
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_SUCCESS))
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(QuitCurrentMessageLoop());
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->model_fetcher_.get() != NULL);
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  msg_loop_.Run();  // EndFetchModel will quit the message loop.
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(service);
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that enabling again doesn't request the model.
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No calls expected.
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(service);
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Check that disabling the service cancels pending requests.
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(*service, ScheduleFetchModel(_))
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(false);
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(service);
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->model_fetcher_.get() != NULL);
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(false);
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(csd_service_->model_fetcher_.get() == NULL);
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  msg_loop_.RunUntilIdle();
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No calls expected.
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(service);
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Requests always return false when the service is disabled.
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ClientPhishingResponse response;
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response.set_phishy(true);
689f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetClientReportPhishingResponse(response.SerializeAsString(), net::HTTP_OK,
690f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                  net::URLRequestStatus::SUCCESS);
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f));
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pending requests also return false if the service is disabled before they
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // report back.
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(*service, ScheduleFetchModel(_))
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(Invoke(service, &MockClientSideDetectionService::Schedule));
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_CALL(*service, EndFetchModel(
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ClientSideDetectionService::MODEL_NOT_CHANGED))
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      .WillOnce(Invoke(service, &MockClientSideDetectionService::Disable));
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  csd_service_->SetEnabledAndRefreshState(true);
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_FALSE(SendClientReportPhishingRequest(GURL("http://a.com/"), 0.4f));
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Mock::VerifyAndClearExpectations(service);
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace safe_browsing
705