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