Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <map>
      6 #include <queue>
      7 #include <string>
      8 
      9 #include "base/callback.h"
     10 #include "base/file_path.h"
     11 #include "base/file_util.h"
     12 #include "base/file_util_proxy.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/memory/scoped_temp_dir.h"
     16 #include "base/message_loop.h"
     17 #include "base/platform_file.h"
     18 #include "base/task.h"
     19 #include "base/time.h"
     20 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
     21 #include "chrome/common/net/test_url_fetcher_factory.h"
     22 #include "chrome/common/net/url_fetcher.h"
     23 #include "chrome/common/safe_browsing/csd.pb.h"
     24 #include "content/browser/browser_thread.h"
     25 #include "googleurl/src/gurl.h"
     26 #include "net/url_request/url_request_status.h"
     27 #include "testing/gtest/include/gtest/gtest.h"
     28 
     29 namespace safe_browsing {
     30 
     31 class ClientSideDetectionServiceTest : public testing::Test {
     32  protected:
     33   virtual void SetUp() {
     34     file_thread_.reset(new BrowserThread(BrowserThread::FILE, &msg_loop_));
     35 
     36     factory_.reset(new FakeURLFetcherFactory());
     37     URLFetcher::set_factory(factory_.get());
     38 
     39     browser_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_));
     40   }
     41 
     42   virtual void TearDown() {
     43     msg_loop_.RunAllPending();
     44     csd_service_.reset();
     45     URLFetcher::set_factory(NULL);
     46     file_thread_.reset();
     47     browser_thread_.reset();
     48   }
     49 
     50   base::PlatformFile GetModelFile() {
     51     model_file_ = base::kInvalidPlatformFileValue;
     52     csd_service_->GetModelFile(NewCallback(
     53         this, &ClientSideDetectionServiceTest::GetModelFileDone));
     54     // This method will block this thread until GetModelFileDone is called.
     55     msg_loop_.Run();
     56     return model_file_;
     57   }
     58 
     59   std::string ReadModelFile(base::PlatformFile model_file) {
     60     char buf[1024];
     61     int n = base::ReadPlatformFile(model_file, 0, buf, 1024);
     62     EXPECT_LE(0, n);
     63     return (n < 0) ? "" : std::string(buf, n);
     64   }
     65 
     66   bool SendClientReportPhishingRequest(const GURL& phishing_url,
     67                                        float score) {
     68     ClientPhishingRequest* request = new ClientPhishingRequest();
     69     request->set_url(phishing_url.spec());
     70     request->set_client_score(score);
     71     request->set_is_phishing(true);  // client thinks the URL is phishing.
     72     csd_service_->SendClientReportPhishingRequest(
     73         request,
     74         NewCallback(this, &ClientSideDetectionServiceTest::SendRequestDone));
     75     phishing_url_ = phishing_url;
     76     msg_loop_.Run();  // Waits until callback is called.
     77     return is_phishing_;
     78   }
     79 
     80   void SetModelFetchResponse(std::string response_data, bool success) {
     81     factory_->SetFakeResponse(ClientSideDetectionService::kClientModelUrl,
     82                               response_data, success);
     83   }
     84 
     85   void SetClientReportPhishingResponse(std::string response_data,
     86                                        bool success) {
     87     factory_->SetFakeResponse(
     88         ClientSideDetectionService::kClientReportPhishingUrl,
     89         response_data, success);
     90   }
     91 
     92   int GetNumReports() {
     93     return csd_service_->GetNumReports();
     94   }
     95 
     96   std::queue<base::Time>& GetPhishingReportTimes() {
     97     return csd_service_->phishing_report_times_;
     98   }
     99 
    100   void SetCache(const GURL& gurl, bool is_phishing, base::Time time) {
    101     csd_service_->cache_[gurl] =
    102         make_linked_ptr(new ClientSideDetectionService::CacheState(is_phishing,
    103                                                                    time));
    104   }
    105 
    106   void TestCache() {
    107     ClientSideDetectionService::PhishingCache& cache = csd_service_->cache_;
    108     base::Time now = base::Time::Now();
    109     base::Time time = now - ClientSideDetectionService::kNegativeCacheInterval +
    110         base::TimeDelta::FromMinutes(5);
    111     cache[GURL("http://first.url.com/")] =
    112         make_linked_ptr(new ClientSideDetectionService::CacheState(false,
    113                                                                    time));
    114 
    115     time = now - ClientSideDetectionService::kNegativeCacheInterval -
    116         base::TimeDelta::FromHours(1);
    117     cache[GURL("http://second.url.com/")] =
    118         make_linked_ptr(new ClientSideDetectionService::CacheState(false,
    119                                                                    time));
    120 
    121     time = now - ClientSideDetectionService::kPositiveCacheInterval -
    122         base::TimeDelta::FromMinutes(5);
    123     cache[GURL("http://third.url.com/")] =
    124         make_linked_ptr(new ClientSideDetectionService::CacheState(true, time));
    125 
    126     time = now - ClientSideDetectionService::kPositiveCacheInterval +
    127         base::TimeDelta::FromMinutes(5);
    128     cache[GURL("http://fourth.url.com/")] =
    129         make_linked_ptr(new ClientSideDetectionService::CacheState(true, time));
    130 
    131     csd_service_->UpdateCache();
    132 
    133     // 3 elements should be in the cache, the first, third, and fourth.
    134     EXPECT_EQ(3U, cache.size());
    135     EXPECT_TRUE(cache.find(GURL("http://first.url.com/")) != cache.end());
    136     EXPECT_TRUE(cache.find(GURL("http://third.url.com/")) != cache.end());
    137     EXPECT_TRUE(cache.find(GURL("http://fourth.url.com/")) != cache.end());
    138 
    139     // While 3 elements remain, only the first and the fourth are actually
    140     // valid.
    141     bool is_phishing;
    142     EXPECT_TRUE(csd_service_->GetValidCachedResult(
    143         GURL("http://first.url.com"), &is_phishing));
    144     EXPECT_FALSE(is_phishing);
    145     EXPECT_FALSE(csd_service_->GetValidCachedResult(
    146         GURL("http://third.url.com"), &is_phishing));
    147     EXPECT_TRUE(csd_service_->GetValidCachedResult(
    148         GURL("http://fourth.url.com"), &is_phishing));
    149     EXPECT_TRUE(is_phishing);
    150   }
    151 
    152  protected:
    153   scoped_ptr<ClientSideDetectionService> csd_service_;
    154   scoped_ptr<FakeURLFetcherFactory> factory_;
    155   MessageLoop msg_loop_;
    156 
    157  private:
    158   void GetModelFileDone(base::PlatformFile model_file) {
    159     model_file_ = model_file;
    160     msg_loop_.Quit();
    161   }
    162 
    163   void SendRequestDone(GURL phishing_url, bool is_phishing) {
    164     ASSERT_EQ(phishing_url, phishing_url_);
    165     is_phishing_ = is_phishing;
    166     msg_loop_.Quit();
    167   }
    168 
    169   scoped_ptr<BrowserThread> browser_thread_;
    170   base::PlatformFile model_file_;
    171   scoped_ptr<BrowserThread> file_thread_;
    172 
    173   GURL phishing_url_;
    174   bool is_phishing_;
    175 };
    176 
    177 TEST_F(ClientSideDetectionServiceTest, TestFetchingModel) {
    178   ScopedTempDir tmp_dir;
    179   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    180   FilePath model_path = tmp_dir.path().AppendASCII("model");
    181 
    182   // The first time we create the csd service the model file does not exist so
    183   // we expect there to be a fetch.
    184   SetModelFetchResponse("BOGUS MODEL", true);
    185   csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL));
    186   base::PlatformFile model_file = GetModelFile();
    187   EXPECT_NE(model_file, base::kInvalidPlatformFileValue);
    188   EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL");
    189 
    190   // If you call GetModelFile() multiple times you always get the same platform
    191   // file back.  We don't re-open the file.
    192   EXPECT_EQ(GetModelFile(), model_file);
    193 
    194   // The second time the model already exists on disk.  In this case there
    195   // should not be any fetch.  To ensure that we clear the factory.
    196   factory_->ClearFakeReponses();
    197   csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL));
    198   model_file = GetModelFile();
    199   EXPECT_NE(model_file, base::kInvalidPlatformFileValue);
    200   EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL");
    201 
    202   // If the model does not exist and the fetch fails we should get an error.
    203   model_path = tmp_dir.path().AppendASCII("another_model");
    204   SetModelFetchResponse("", false /* success */);
    205   csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL));
    206   EXPECT_EQ(GetModelFile(), base::kInvalidPlatformFileValue);
    207 }
    208 
    209 TEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) {
    210   SetModelFetchResponse("bogus model", true /* success */);
    211   ScopedTempDir tmp_dir;
    212   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    213   csd_service_.reset(ClientSideDetectionService::Create(
    214       tmp_dir.path().AppendASCII("model"), NULL));
    215   EXPECT_TRUE(csd_service_.get() != NULL);
    216   // We delete the client-side detection service class even though the callbacks
    217   // haven't run yet.
    218   csd_service_.reset();
    219   // Waiting for the callbacks to run should not crash even if the service
    220   // object is gone.
    221   msg_loop_.RunAllPending();
    222 }
    223 
    224 TEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) {
    225   SetModelFetchResponse("bogus model", true /* success */);
    226   ScopedTempDir tmp_dir;
    227   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    228   csd_service_.reset(ClientSideDetectionService::Create(
    229       tmp_dir.path().AppendASCII("model"), NULL));
    230 
    231   GURL url("http://a.com/");
    232   float score = 0.4f;  // Some random client score.
    233 
    234   base::Time before = base::Time::Now();
    235 
    236   // Invalid response body from the server.
    237   SetClientReportPhishingResponse("invalid proto response", true /* success */);
    238   EXPECT_FALSE(SendClientReportPhishingRequest(url, score));
    239 
    240   // Normal behavior.
    241   ClientPhishingResponse response;
    242   response.set_phishy(true);
    243   SetClientReportPhishingResponse(response.SerializeAsString(),
    244                                   true /* success */);
    245   EXPECT_TRUE(SendClientReportPhishingRequest(url, score));
    246 
    247   // This request will fail
    248   GURL second_url("http://b.com/");
    249   response.set_phishy(false);
    250   SetClientReportPhishingResponse(response.SerializeAsString(),
    251                                   false /* success*/);
    252   EXPECT_FALSE(SendClientReportPhishingRequest(second_url, score));
    253 
    254   base::Time after = base::Time::Now();
    255 
    256   // Check that we have recorded all 3 requests within the correct time range.
    257   std::queue<base::Time>& report_times = GetPhishingReportTimes();
    258   EXPECT_EQ(3U, report_times.size());
    259   while (!report_times.empty()) {
    260     base::Time time = report_times.back();
    261     report_times.pop();
    262     EXPECT_LE(before, time);
    263     EXPECT_GE(after, time);
    264   }
    265 
    266   // Only the first url should be in the cache.
    267   bool is_phishing;
    268   EXPECT_TRUE(csd_service_->IsInCache(url));
    269   EXPECT_TRUE(csd_service_->GetValidCachedResult(url, &is_phishing));
    270   EXPECT_TRUE(is_phishing);
    271   EXPECT_FALSE(csd_service_->IsInCache(second_url));
    272 }
    273 
    274 TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) {
    275   SetModelFetchResponse("bogus model", true /* success */);
    276   ScopedTempDir tmp_dir;
    277   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    278   csd_service_.reset(ClientSideDetectionService::Create(
    279       tmp_dir.path().AppendASCII("model"), NULL));
    280 
    281   std::queue<base::Time>& report_times = GetPhishingReportTimes();
    282   base::Time now = base::Time::Now();
    283   base::TimeDelta twenty_five_hours = base::TimeDelta::FromHours(25);
    284   report_times.push(now - twenty_five_hours);
    285   report_times.push(now - twenty_five_hours);
    286   report_times.push(now);
    287   report_times.push(now);
    288 
    289   EXPECT_EQ(2, GetNumReports());
    290 }
    291 
    292 TEST_F(ClientSideDetectionServiceTest, CacheTest) {
    293   SetModelFetchResponse("bogus model", true /* success */);
    294   ScopedTempDir tmp_dir;
    295   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    296   csd_service_.reset(ClientSideDetectionService::Create(
    297       tmp_dir.path().AppendASCII("model"), NULL));
    298 
    299   TestCache();
    300 }
    301 
    302 TEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
    303   SetModelFetchResponse("bogus model", true /* success */);
    304   ScopedTempDir tmp_dir;
    305   ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
    306   csd_service_.reset(ClientSideDetectionService::Create(
    307       tmp_dir.path().AppendASCII("model"), NULL));
    308 
    309   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3"));
    310   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("127.0.0.1"));
    311   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("172.24.3.4"));
    312   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("192.168.1.1"));
    313   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fc00::"));
    314   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0::"));
    315   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0:1:2::3"));
    316   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("::1"));
    317 
    318   EXPECT_FALSE(csd_service_->IsPrivateIPAddress("1.2.3.4"));
    319   EXPECT_FALSE(csd_service_->IsPrivateIPAddress("200.1.1.1"));
    320   EXPECT_FALSE(csd_service_->IsPrivateIPAddress("2001:0db8:ac10:fe01::"));
    321 
    322   // If the address can't be parsed, the default is true.
    323   EXPECT_TRUE(csd_service_->IsPrivateIPAddress("blah"));
    324 }
    325 
    326 }  // namespace safe_browsing
    327