1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
24a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
34a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// found in the LICENSE file.
44a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
54a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <map>
672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include <queue>
74a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <string>
84a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
94a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/callback.h"
104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/file_path.h"
114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/file_util.h"
124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/file_util_proxy.h"
134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/logging.h"
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h"
164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/message_loop.h"
174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/platform_file.h"
184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/task.h"
1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/time.h"
204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/safe_browsing/client_side_detection_service.h"
214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/common/net/test_url_fetcher_factory.h"
224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/common/net/url_fetcher.h"
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/safe_browsing/csd.pb.h"
24dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h"
254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "googleurl/src/gurl.h"
264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "net/url_request/url_request_status.h"
27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "testing/gtest/include/gtest/gtest.h"
284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace safe_browsing {
304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass ClientSideDetectionServiceTest : public testing::Test {
324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch protected:
334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  virtual void SetUp() {
344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    file_thread_.reset(new BrowserThread(BrowserThread::FILE, &msg_loop_));
354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    factory_.reset(new FakeURLFetcherFactory());
374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    URLFetcher::set_factory(factory_.get());
384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    browser_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_));
404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  virtual void TearDown() {
434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    msg_loop_.RunAllPending();
444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    csd_service_.reset();
454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    URLFetcher::set_factory(NULL);
464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    file_thread_.reset();
474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    browser_thread_.reset();
484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  base::PlatformFile GetModelFile() {
514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    model_file_ = base::kInvalidPlatformFileValue;
524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    csd_service_->GetModelFile(NewCallback(
534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        this, &ClientSideDetectionServiceTest::GetModelFileDone));
544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // This method will block this thread until GetModelFileDone is called.
554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    msg_loop_.Run();
564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return model_file_;
574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::string ReadModelFile(base::PlatformFile model_file) {
604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    char buf[1024];
614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    int n = base::ReadPlatformFile(model_file, 0, buf, 1024);
624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    EXPECT_LE(0, n);
634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return (n < 0) ? "" : std::string(buf, n);
644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  bool SendClientReportPhishingRequest(const GURL& phishing_url,
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                       float score) {
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ClientPhishingRequest* request = new ClientPhishingRequest();
69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    request->set_url(phishing_url.spec());
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    request->set_client_score(score);
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    request->set_is_phishing(true);  // client thinks the URL is phishing.
724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    csd_service_->SendClientReportPhishingRequest(
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        request,
744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        NewCallback(this, &ClientSideDetectionServiceTest::SendRequestDone));
754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    phishing_url_ = phishing_url;
764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    msg_loop_.Run();  // Waits until callback is called.
774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return is_phishing_;
784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  void SetModelFetchResponse(std::string response_data, bool success) {
814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    factory_->SetFakeResponse(ClientSideDetectionService::kClientModelUrl,
824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                              response_data, success);
834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  void SetClientReportPhishingResponse(std::string response_data,
864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                       bool success) {
874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    factory_->SetFakeResponse(
884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        ClientSideDetectionService::kClientReportPhishingUrl,
894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        response_data, success);
904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  int GetNumReports() {
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return csd_service_->GetNumReports();
9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
9572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
9672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::queue<base::Time>& GetPhishingReportTimes() {
9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return csd_service_->phishing_report_times_;
9872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
9972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
10072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void SetCache(const GURL& gurl, bool is_phishing, base::Time time) {
10172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    csd_service_->cache_[gurl] =
10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        make_linked_ptr(new ClientSideDetectionService::CacheState(is_phishing,
10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                                   time));
10472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
10572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void TestCache() {
10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ClientSideDetectionService::PhishingCache& cache = csd_service_->cache_;
10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::Time now = base::Time::Now();
10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::Time time = now - ClientSideDetectionService::kNegativeCacheInterval +
11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        base::TimeDelta::FromMinutes(5);
11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    cache[GURL("http://first.url.com/")] =
11272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        make_linked_ptr(new ClientSideDetectionService::CacheState(false,
11372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                                   time));
11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
11572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    time = now - ClientSideDetectionService::kNegativeCacheInterval -
11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        base::TimeDelta::FromHours(1);
11772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    cache[GURL("http://second.url.com/")] =
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        make_linked_ptr(new ClientSideDetectionService::CacheState(false,
11972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                                   time));
12072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
12172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    time = now - ClientSideDetectionService::kPositiveCacheInterval -
12272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        base::TimeDelta::FromMinutes(5);
12372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    cache[GURL("http://third.url.com/")] =
12472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        make_linked_ptr(new ClientSideDetectionService::CacheState(true, time));
12572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    time = now - ClientSideDetectionService::kPositiveCacheInterval +
12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        base::TimeDelta::FromMinutes(5);
12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    cache[GURL("http://fourth.url.com/")] =
12972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        make_linked_ptr(new ClientSideDetectionService::CacheState(true, time));
13072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    csd_service_->UpdateCache();
13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // 3 elements should be in the cache, the first, third, and fourth.
13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_EQ(3U, cache.size());
13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_TRUE(cache.find(GURL("http://first.url.com/")) != cache.end());
13672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_TRUE(cache.find(GURL("http://third.url.com/")) != cache.end());
13772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_TRUE(cache.find(GURL("http://fourth.url.com/")) != cache.end());
13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // While 3 elements remain, only the first and the fourth are actually
14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // valid.
14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    bool is_phishing;
142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    EXPECT_TRUE(csd_service_->GetValidCachedResult(
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        GURL("http://first.url.com"), &is_phishing));
14472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_FALSE(is_phishing);
145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    EXPECT_FALSE(csd_service_->GetValidCachedResult(
146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        GURL("http://third.url.com"), &is_phishing));
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    EXPECT_TRUE(csd_service_->GetValidCachedResult(
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        GURL("http://fourth.url.com"), &is_phishing));
14972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_TRUE(is_phishing);
15072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
15172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
1524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch protected:
1534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<ClientSideDetectionService> csd_service_;
1544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<FakeURLFetcherFactory> factory_;
1554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  MessageLoop msg_loop_;
1564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch private:
1584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  void GetModelFileDone(base::PlatformFile model_file) {
1594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    model_file_ = model_file;
1604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    msg_loop_.Quit();
1614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  void SendRequestDone(GURL phishing_url, bool is_phishing) {
1644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    ASSERT_EQ(phishing_url, phishing_url_);
1654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    is_phishing_ = is_phishing;
1664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    msg_loop_.Quit();
1674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<BrowserThread> browser_thread_;
1704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  base::PlatformFile model_file_;
1714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<BrowserThread> file_thread_;
1724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  GURL phishing_url_;
1744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  bool is_phishing_;
1754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch};
1764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochTEST_F(ClientSideDetectionServiceTest, TestFetchingModel) {
1784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ScopedTempDir tmp_dir;
1794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
1804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  FilePath model_path = tmp_dir.path().AppendASCII("model");
1814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // The first time we create the csd service the model file does not exist so
1834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // we expect there to be a fetch.
1844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SetModelFetchResponse("BOGUS MODEL", true);
1854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL));
1864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  base::PlatformFile model_file = GetModelFile();
1874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_NE(model_file, base::kInvalidPlatformFileValue);
1884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL");
1894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // If you call GetModelFile() multiple times you always get the same platform
1914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // file back.  We don't re-open the file.
1924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_EQ(GetModelFile(), model_file);
1934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // The second time the model already exists on disk.  In this case there
1954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // should not be any fetch.  To ensure that we clear the factory.
1964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  factory_->ClearFakeReponses();
1974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL));
1984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  model_file = GetModelFile();
1994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_NE(model_file, base::kInvalidPlatformFileValue);
2004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL");
2014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // If the model does not exist and the fetch fails we should get an error.
2034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  model_path = tmp_dir.path().AppendASCII("another_model");
2044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SetModelFetchResponse("", false /* success */);
2054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL));
2064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_EQ(GetModelFile(), base::kInvalidPlatformFileValue);
2074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochTEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) {
2104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SetModelFetchResponse("bogus model", true /* success */);
2114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ScopedTempDir tmp_dir;
2124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
2134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  csd_service_.reset(ClientSideDetectionService::Create(
2144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      tmp_dir.path().AppendASCII("model"), NULL));
2154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EXPECT_TRUE(csd_service_.get() != NULL);
2164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // We delete the client-side detection service class even though the callbacks
2174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // haven't run yet.
2184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  csd_service_.reset();
2194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Waiting for the callbacks to run should not crash even if the service
2204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // object is gone.
2214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  msg_loop_.RunAllPending();
2224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
2234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochTEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) {
2254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SetModelFetchResponse("bogus model", true /* success */);
2264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ScopedTempDir tmp_dir;
2274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
2284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  csd_service_.reset(ClientSideDetectionService::Create(
2294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      tmp_dir.path().AppendASCII("model"), NULL));
2304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  GURL url("http://a.com/");
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  float score = 0.4f;  // Some random client score.
2334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Time before = base::Time::Now();
2354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Invalid response body from the server.
2374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SetClientReportPhishingResponse("invalid proto response", true /* success */);
23872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(SendClientReportPhishingRequest(url, score));
2394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Normal behavior.
2414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ClientPhishingResponse response;
2424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  response.set_phishy(true);
2434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  SetClientReportPhishingResponse(response.SerializeAsString(),
2444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                  true /* success */);
24572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_TRUE(SendClientReportPhishingRequest(url, score));
24672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // This request will fail
24872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  GURL second_url("http://b.com/");
24972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  response.set_phishy(false);
25072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetClientReportPhishingResponse(response.SerializeAsString(),
25172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                  false /* success*/);
25272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(SendClientReportPhishingRequest(second_url, score));
25372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
25472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Time after = base::Time::Now();
25572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Check that we have recorded all 3 requests within the correct time range.
25772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::queue<base::Time>& report_times = GetPhishingReportTimes();
258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_EQ(3U, report_times.size());
25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  while (!report_times.empty()) {
26072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::Time time = report_times.back();
26172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    report_times.pop();
26272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_LE(before, time);
26372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    EXPECT_GE(after, time);
26472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Only the first url should be in the cache.
267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool is_phishing;
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(csd_service_->IsInCache(url));
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(csd_service_->GetValidCachedResult(url, &is_phishing));
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(is_phishing);
271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_FALSE(csd_service_->IsInCache(second_url));
27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenTEST_F(ClientSideDetectionServiceTest, GetNumReportTest) {
27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetModelFetchResponse("bogus model", true /* success */);
27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ScopedTempDir tmp_dir;
27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  csd_service_.reset(ClientSideDetectionService::Create(
27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      tmp_dir.path().AppendASCII("model"), NULL));
28072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
28172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::queue<base::Time>& report_times = GetPhishingReportTimes();
28272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Time now = base::Time::Now();
28372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::TimeDelta twenty_five_hours = base::TimeDelta::FromHours(25);
28472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  report_times.push(now - twenty_five_hours);
28572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  report_times.push(now - twenty_five_hours);
28672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  report_times.push(now);
28772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  report_times.push(now);
28872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
28972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_EQ(2, GetNumReports());
29072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
29172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
29272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenTEST_F(ClientSideDetectionServiceTest, CacheTest) {
29372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetModelFetchResponse("bogus model", true /* success */);
29472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ScopedTempDir tmp_dir;
29572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
29672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  csd_service_.reset(ClientSideDetectionService::Create(
29772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      tmp_dir.path().AppendASCII("model"), NULL));
29872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  TestCache();
30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
302dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenTEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
303dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  SetModelFetchResponse("bogus model", true /* success */);
30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ScopedTempDir tmp_dir;
30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
306dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  csd_service_.reset(ClientSideDetectionService::Create(
307dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      tmp_dir.path().AppendASCII("model"), NULL));
30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
309dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3"));
310dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("127.0.0.1"));
311dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("172.24.3.4"));
312dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("192.168.1.1"));
313dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fc00::"));
314dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0::"));
315dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0:1:2::3"));
316dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("::1"));
317dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
318dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("1.2.3.4"));
319dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("200.1.1.1"));
320dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(csd_service_->IsPrivateIPAddress("2001:0db8:ac10:fe01::"));
321dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
322dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // If the address can't be parsed, the default is true.
323dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(csd_service_->IsPrivateIPAddress("blah"));
3244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
3254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
3264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace safe_browsing
327