Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright 2013 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 "chrome/browser/safe_browsing/download_feedback_service.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/run_loop.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "chrome/browser/safe_browsing/download_feedback.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/test/mock_download_item.h"
     14 #include "content/public/test/test_browser_thread_bundle.h"
     15 #include "net/url_request/url_request_test_util.h"
     16 #include "testing/gmock/include/gmock/gmock.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 using ::testing::_;
     20 using ::testing::Return;
     21 using ::testing::SaveArg;
     22 
     23 namespace safe_browsing {
     24 
     25 namespace {
     26 
     27 class FakeDownloadFeedback : public DownloadFeedback {
     28  public:
     29   FakeDownloadFeedback(net::URLRequestContextGetter* request_context_getter,
     30                        base::TaskRunner* file_task_runner,
     31                        const base::FilePath& file_path,
     32                        const std::string& ping_request,
     33                        const std::string& ping_response,
     34                        base::Closure deletion_callback)
     35       : ping_request_(ping_request),
     36         ping_response_(ping_response),
     37         deletion_callback_(deletion_callback),
     38         start_called_(false) {
     39   }
     40 
     41   virtual ~FakeDownloadFeedback() {
     42     deletion_callback_.Run();
     43   }
     44 
     45   virtual void Start(const base::Closure& finish_callback) OVERRIDE {
     46     start_called_ = true;
     47     finish_callback_ = finish_callback;
     48   }
     49 
     50   virtual const std::string& GetPingRequestForTesting() const OVERRIDE {
     51     return ping_request_;
     52   }
     53 
     54   virtual const std::string& GetPingResponseForTesting() const OVERRIDE {
     55     return ping_response_;
     56   }
     57 
     58   base::Closure finish_callback() const {
     59     return finish_callback_;
     60   }
     61 
     62   bool start_called() const {
     63     return start_called_;
     64   }
     65 
     66  private:
     67   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
     68   scoped_refptr<base::TaskRunner> file_task_runner_;
     69   base::FilePath file_path_;
     70   std::string ping_request_;
     71   std::string ping_response_;
     72 
     73   base::Closure finish_callback_;
     74   base::Closure deletion_callback_;
     75   bool start_called_;
     76 };
     77 
     78 class FakeDownloadFeedbackFactory : public DownloadFeedbackFactory {
     79  public:
     80   virtual ~FakeDownloadFeedbackFactory() {}
     81 
     82   virtual DownloadFeedback* CreateDownloadFeedback(
     83       net::URLRequestContextGetter* request_context_getter,
     84       base::TaskRunner* file_task_runner,
     85       const base::FilePath& file_path,
     86       const std::string& ping_request,
     87       const std::string& ping_response) OVERRIDE {
     88     FakeDownloadFeedback* feedback = new FakeDownloadFeedback(
     89         request_context_getter,
     90         file_task_runner,
     91         file_path,
     92         ping_request,
     93         ping_response,
     94         base::Bind(&FakeDownloadFeedbackFactory::DownloadFeedbackDeleted,
     95                    base::Unretained(this),
     96                    feedbacks_.size()));
     97     feedbacks_.push_back(feedback);
     98     return feedback;
     99   }
    100 
    101   void DownloadFeedbackDeleted(size_t n) {
    102     feedbacks_[n] = NULL;
    103   }
    104 
    105   FakeDownloadFeedback* feedback(size_t n) const {
    106     return feedbacks_[n];
    107   }
    108 
    109   size_t num_feedbacks() const {
    110     return feedbacks_.size();
    111   }
    112 
    113  private:
    114   std::vector<FakeDownloadFeedback*> feedbacks_;
    115 };
    116 
    117 bool WillStorePings(DownloadProtectionService::DownloadCheckResult result,
    118                      int64 size) {
    119   content::MockDownloadItem item;
    120   EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(size));
    121 
    122   EXPECT_FALSE(DownloadFeedbackService::IsEnabledForDownload(item));
    123   DownloadFeedbackService::MaybeStorePingsForDownload(result, &item, "a", "b");
    124   return DownloadFeedbackService::IsEnabledForDownload(item);
    125 }
    126 
    127 }  // namespace
    128 
    129 class DownloadFeedbackServiceTest : public testing::Test {
    130  public:
    131   DownloadFeedbackServiceTest()
    132       : file_task_runner_(content::BrowserThread::GetMessageLoopProxyForThread(
    133             content::BrowserThread::FILE)),
    134         io_task_runner_(content::BrowserThread::GetMessageLoopProxyForThread(
    135             content::BrowserThread::IO)),
    136         request_context_getter_(
    137             new net::TestURLRequestContextGetter(io_task_runner_)) {
    138   }
    139 
    140   virtual void SetUp() OVERRIDE {
    141     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    142     DownloadFeedback::RegisterFactory(&download_feedback_factory_);
    143   }
    144 
    145   virtual void TearDown() OVERRIDE {
    146     DownloadFeedback::RegisterFactory(NULL);
    147   }
    148 
    149   base::FilePath CreateTestFile(int n) const {
    150     base::FilePath upload_file_path(
    151         temp_dir_.path().AppendASCII("test file " + base::IntToString(n)));
    152     const std::string upload_file_data = "data";
    153     int wrote = file_util::WriteFile(
    154         upload_file_path, upload_file_data.data(), upload_file_data.size());
    155     EXPECT_EQ(static_cast<int>(upload_file_data.size()), wrote);
    156     return upload_file_path;
    157   }
    158 
    159   FakeDownloadFeedback* feedback(size_t n) const {
    160     return download_feedback_factory_.feedback(n);
    161   }
    162 
    163   size_t num_feedbacks() const {
    164     return download_feedback_factory_.num_feedbacks();
    165   }
    166  protected:
    167   base::ScopedTempDir temp_dir_;
    168   content::TestBrowserThreadBundle thread_bundle_;
    169   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
    170   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
    171   scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
    172   FakeDownloadFeedbackFactory download_feedback_factory_;
    173 
    174 };
    175 
    176 TEST_F(DownloadFeedbackServiceTest, MaybeStorePingsForDownload) {
    177   const int64 ok_size = DownloadFeedback::kMaxUploadSize;
    178   const int64 bad_size = DownloadFeedback::kMaxUploadSize + 1;
    179 
    180   EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE, ok_size));
    181   EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS, ok_size));
    182   EXPECT_TRUE(WillStorePings(DownloadProtectionService::UNCOMMON, ok_size));
    183   EXPECT_TRUE(
    184       WillStorePings(DownloadProtectionService::DANGEROUS_HOST, ok_size));
    185 
    186   EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE, bad_size));
    187   EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS, bad_size));
    188   EXPECT_FALSE(WillStorePings(DownloadProtectionService::UNCOMMON, bad_size));
    189   EXPECT_FALSE(
    190       WillStorePings(DownloadProtectionService::DANGEROUS_HOST, bad_size));
    191 }
    192 
    193 TEST_F(DownloadFeedbackServiceTest, SingleFeedbackComplete) {
    194   const base::FilePath file_path(CreateTestFile(0));
    195   const std::string ping_request = "ping";
    196   const std::string ping_response = "resp";
    197 
    198   content::DownloadItem::AcquireFileCallback download_discarded_callback;
    199 
    200   content::MockDownloadItem item;
    201   EXPECT_CALL(item, GetDangerType())
    202       .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT));
    203   EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(1000));
    204   EXPECT_CALL(item, StealDangerousDownload(_))
    205       .WillOnce(SaveArg<0>(&download_discarded_callback));
    206 
    207   DownloadFeedbackService service(request_context_getter_.get(),
    208                                   file_task_runner_.get());
    209   service.MaybeStorePingsForDownload(
    210       DownloadProtectionService::UNCOMMON, &item, ping_request, ping_response);
    211   ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item));
    212   service.BeginFeedbackForDownload(&item);
    213   ASSERT_FALSE(download_discarded_callback.is_null());
    214   EXPECT_EQ(0U, num_feedbacks());
    215 
    216   download_discarded_callback.Run(file_path);
    217   ASSERT_EQ(1U, num_feedbacks());
    218   ASSERT_TRUE(feedback(0));
    219   EXPECT_TRUE(feedback(0)->start_called());
    220   EXPECT_EQ(ping_request, feedback(0)->GetPingRequestForTesting());
    221   EXPECT_EQ(ping_response, feedback(0)->GetPingResponseForTesting());
    222 
    223   feedback(0)->finish_callback().Run();
    224   EXPECT_FALSE(feedback(0));
    225 
    226   // File should still exist since our FakeDownloadFeedback does not delete it.
    227   base::RunLoop().RunUntilIdle();
    228   EXPECT_TRUE(base::PathExists(file_path));
    229 }
    230 
    231 TEST_F(DownloadFeedbackServiceTest, MultiplePendingFeedbackComplete) {
    232   const std::string ping_request = "ping";
    233   const std::string ping_response = "resp";
    234   const size_t num_downloads = 3;
    235 
    236   content::DownloadItem::AcquireFileCallback
    237       download_discarded_callback[num_downloads];
    238 
    239   base::FilePath file_path[num_downloads];
    240   content::MockDownloadItem item[num_downloads];
    241   for (size_t i = 0; i < num_downloads; ++i) {
    242     file_path[i] = CreateTestFile(i);
    243     EXPECT_CALL(item[i], GetDangerType())
    244         .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT));
    245     EXPECT_CALL(item[i], GetReceivedBytes()).WillRepeatedly(Return(1000));
    246     EXPECT_CALL(item[i], StealDangerousDownload(_))
    247         .WillOnce(SaveArg<0>(&download_discarded_callback[i]));
    248     DownloadFeedbackService::MaybeStorePingsForDownload(
    249         DownloadProtectionService::UNCOMMON, &item[i], ping_request,
    250         ping_response);
    251     ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item[i]));
    252   }
    253 
    254   {
    255     DownloadFeedbackService service(request_context_getter_.get(),
    256                                     file_task_runner_.get());
    257     for (size_t i = 0; i < num_downloads; ++i) {
    258       SCOPED_TRACE(i);
    259       service.BeginFeedbackForDownload(&item[i]);
    260       ASSERT_FALSE(download_discarded_callback[i].is_null());
    261     }
    262     EXPECT_EQ(0U, num_feedbacks());
    263 
    264     for (size_t i = 0; i < num_downloads; ++i) {
    265       download_discarded_callback[i].Run(file_path[i]);
    266     }
    267 
    268     ASSERT_EQ(3U, num_feedbacks());
    269     EXPECT_TRUE(feedback(0)->start_called());
    270     EXPECT_FALSE(feedback(1)->start_called());
    271     EXPECT_FALSE(feedback(2)->start_called());
    272 
    273     feedback(0)->finish_callback().Run();
    274 
    275     EXPECT_FALSE(feedback(0));
    276     EXPECT_TRUE(feedback(1)->start_called());
    277     EXPECT_FALSE(feedback(2)->start_called());
    278 
    279     feedback(1)->finish_callback().Run();
    280 
    281     EXPECT_FALSE(feedback(0));
    282     EXPECT_FALSE(feedback(1));
    283     EXPECT_TRUE(feedback(2)->start_called());
    284 
    285     feedback(2)->finish_callback().Run();
    286 
    287     EXPECT_FALSE(feedback(0));
    288     EXPECT_FALSE(feedback(1));
    289     EXPECT_FALSE(feedback(2));
    290   }
    291 
    292   base::RunLoop().RunUntilIdle();
    293   // These files should still exist since the FakeDownloadFeedback does not
    294   // delete them.
    295   EXPECT_TRUE(base::PathExists(file_path[0]));
    296   EXPECT_TRUE(base::PathExists(file_path[1]));
    297   EXPECT_TRUE(base::PathExists(file_path[2]));
    298 }
    299 
    300 TEST_F(DownloadFeedbackServiceTest, MultiFeedbackWithIncomplete) {
    301   const std::string ping_request = "ping";
    302   const std::string ping_response = "resp";
    303   const size_t num_downloads = 3;
    304 
    305   content::DownloadItem::AcquireFileCallback
    306       download_discarded_callback[num_downloads];
    307 
    308   base::FilePath file_path[num_downloads];
    309   content::MockDownloadItem item[num_downloads];
    310   for (size_t i = 0; i < num_downloads; ++i) {
    311     file_path[i] = CreateTestFile(i);
    312     EXPECT_CALL(item[i], GetDangerType())
    313         .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT));
    314     EXPECT_CALL(item[i], GetReceivedBytes()).WillRepeatedly(Return(1000));
    315     EXPECT_CALL(item[i], StealDangerousDownload(_))
    316         .WillOnce(SaveArg<0>(&download_discarded_callback[i]));
    317     DownloadFeedbackService::MaybeStorePingsForDownload(
    318         DownloadProtectionService::UNCOMMON, &item[i], ping_request,
    319         ping_response);
    320     ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item[i]));
    321   }
    322 
    323   {
    324     DownloadFeedbackService service(request_context_getter_.get(),
    325                                     file_task_runner_.get());
    326     for (size_t i = 0; i < num_downloads; ++i) {
    327       SCOPED_TRACE(i);
    328       service.BeginFeedbackForDownload(&item[i]);
    329       ASSERT_FALSE(download_discarded_callback[i].is_null());
    330     }
    331     EXPECT_EQ(0U, num_feedbacks());
    332 
    333     download_discarded_callback[0].Run(file_path[0]);
    334     ASSERT_EQ(1U, num_feedbacks());
    335     ASSERT_TRUE(feedback(0));
    336     EXPECT_TRUE(feedback(0)->start_called());
    337 
    338     download_discarded_callback[1].Run(file_path[1]);
    339     ASSERT_EQ(2U, num_feedbacks());
    340     ASSERT_TRUE(feedback(1));
    341     EXPECT_FALSE(feedback(1)->start_called());
    342 
    343     feedback(0)->finish_callback().Run();
    344     EXPECT_FALSE(feedback(0));
    345     EXPECT_TRUE(feedback(1)->start_called());
    346   }
    347 
    348   EXPECT_EQ(2U, num_feedbacks());
    349   for (size_t i = 0; i < num_feedbacks(); ++i) {
    350     SCOPED_TRACE(i);
    351     EXPECT_FALSE(feedback(i));
    352   }
    353 
    354   // Running a download acquired callback after the DownloadFeedbackService is
    355   // destroyed should delete the file.
    356   download_discarded_callback[2].Run(file_path[2]);
    357   EXPECT_EQ(2U, num_feedbacks());
    358 
    359   // File should still exist since the FileUtilProxy task hasn't run yet.
    360   EXPECT_TRUE(base::PathExists(file_path[2]));
    361 
    362   base::RunLoop().RunUntilIdle();
    363   // File should be deleted since the AcquireFileCallback ran after the service
    364   // was deleted.
    365   EXPECT_FALSE(base::PathExists(file_path[2]));
    366 
    367   // These files should still exist since the FakeDownloadFeedback does not
    368   // delete them.
    369   EXPECT_TRUE(base::PathExists(file_path[0]));
    370   EXPECT_TRUE(base::PathExists(file_path[1]));
    371 }
    372 
    373 }  // namespace safe_browsing
    374