• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <vector>
8 
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "chrome/browser/safe_browsing/download_feedback.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:
FakeDownloadFeedback(net::URLRequestContextGetter * request_context_getter,base::TaskRunner * file_task_runner,const base::FilePath & file_path,const std::string & ping_request,const std::string & ping_response,base::Closure deletion_callback)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 
~FakeDownloadFeedback()43   virtual ~FakeDownloadFeedback() {
44     deletion_callback_.Run();
45   }
46 
Start(const base::Closure & finish_callback)47   virtual void Start(const base::Closure& finish_callback) OVERRIDE {
48     start_called_ = true;
49     finish_callback_ = finish_callback;
50   }
51 
GetPingRequestForTesting() const52   virtual const std::string& GetPingRequestForTesting() const OVERRIDE {
53     return ping_request_;
54   }
55 
GetPingResponseForTesting() const56   virtual const std::string& GetPingResponseForTesting() const OVERRIDE {
57     return ping_response_;
58   }
59 
finish_callback() const60   base::Closure finish_callback() const {
61     return finish_callback_;
62   }
63 
start_called() const64   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:
~FakeDownloadFeedbackFactory()82   virtual ~FakeDownloadFeedbackFactory() {}
83 
CreateDownloadFeedback(net::URLRequestContextGetter * request_context_getter,base::TaskRunner * file_task_runner,const base::FilePath & file_path,const std::string & ping_request,const std::string & ping_response)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 
DownloadFeedbackDeleted(size_t n)103   void DownloadFeedbackDeleted(size_t n) {
104     feedbacks_[n] = NULL;
105   }
106 
feedback(size_t n) const107   FakeDownloadFeedback* feedback(size_t n) const {
108     return feedbacks_[n];
109   }
110 
num_feedbacks() const111   size_t num_feedbacks() const {
112     return feedbacks_.size();
113   }
114 
115  private:
116   std::vector<FakeDownloadFeedback*> feedbacks_;
117 };
118 
WillStorePings(DownloadProtectionService::DownloadCheckResult result,int64 size)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:
DownloadFeedbackServiceTest()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 
SetUp()142   virtual void SetUp() OVERRIDE {
143     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
144     DownloadFeedback::RegisterFactory(&download_feedback_factory_);
145   }
146 
TearDown()147   virtual void TearDown() OVERRIDE {
148     DownloadFeedback::RegisterFactory(NULL);
149   }
150 
CreateTestFile(int n) const151   base::FilePath CreateTestFile(int n) const {
152     base::FilePath upload_file_path(
153         temp_dir_.path().AppendASCII("test file " + base::IntToString(n)));
154     const std::string upload_file_data = "data";
155     int wrote = base::WriteFile(
156         upload_file_path, upload_file_data.data(), upload_file_data.size());
157     EXPECT_EQ(static_cast<int>(upload_file_data.size()), wrote);
158     return upload_file_path;
159   }
160 
feedback(size_t n) const161   FakeDownloadFeedback* feedback(size_t n) const {
162     return download_feedback_factory_.feedback(n);
163   }
164 
num_feedbacks() const165   size_t num_feedbacks() const {
166     return download_feedback_factory_.num_feedbacks();
167   }
168 
169  protected:
170   base::ScopedTempDir temp_dir_;
171   content::TestBrowserThreadBundle thread_bundle_;
172   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
173   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
174   scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
175   FakeDownloadFeedbackFactory download_feedback_factory_;
176 };
177 
TEST_F(DownloadFeedbackServiceTest,MaybeStorePingsForDownload)178 TEST_F(DownloadFeedbackServiceTest, MaybeStorePingsForDownload) {
179   const int64 ok_size = DownloadFeedback::kMaxUploadSize;
180   const int64 bad_size = DownloadFeedback::kMaxUploadSize + 1;
181 
182   EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE, ok_size));
183   EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS, ok_size));
184   EXPECT_TRUE(WillStorePings(DownloadProtectionService::UNCOMMON, ok_size));
185   EXPECT_TRUE(
186       WillStorePings(DownloadProtectionService::DANGEROUS_HOST, ok_size));
187 
188   EXPECT_FALSE(WillStorePings(DownloadProtectionService::SAFE, bad_size));
189   EXPECT_FALSE(WillStorePings(DownloadProtectionService::DANGEROUS, bad_size));
190   EXPECT_FALSE(WillStorePings(DownloadProtectionService::UNCOMMON, bad_size));
191   EXPECT_FALSE(
192       WillStorePings(DownloadProtectionService::DANGEROUS_HOST, bad_size));
193 }
194 
TEST_F(DownloadFeedbackServiceTest,SingleFeedbackComplete)195 TEST_F(DownloadFeedbackServiceTest, SingleFeedbackComplete) {
196   const base::FilePath file_path(CreateTestFile(0));
197   const std::string ping_request = "ping";
198   const std::string ping_response = "resp";
199 
200   content::DownloadItem::AcquireFileCallback download_discarded_callback;
201 
202   content::MockDownloadItem item;
203   EXPECT_CALL(item, GetDangerType())
204       .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT));
205   EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(1000));
206   EXPECT_CALL(item, StealDangerousDownload(_))
207       .WillOnce(SaveArg<0>(&download_discarded_callback));
208 
209   DownloadFeedbackService service(request_context_getter_.get(),
210                                   file_task_runner_.get());
211   service.MaybeStorePingsForDownload(
212       DownloadProtectionService::UNCOMMON, &item, ping_request, ping_response);
213   ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item));
214   service.BeginFeedbackForDownload(&item);
215   ASSERT_FALSE(download_discarded_callback.is_null());
216   EXPECT_EQ(0U, num_feedbacks());
217 
218   download_discarded_callback.Run(file_path);
219   ASSERT_EQ(1U, num_feedbacks());
220   ASSERT_TRUE(feedback(0));
221   EXPECT_TRUE(feedback(0)->start_called());
222   EXPECT_EQ(ping_request, feedback(0)->GetPingRequestForTesting());
223   EXPECT_EQ(ping_response, feedback(0)->GetPingResponseForTesting());
224 
225   feedback(0)->finish_callback().Run();
226   EXPECT_FALSE(feedback(0));
227 
228   // File should still exist since our FakeDownloadFeedback does not delete it.
229   base::RunLoop().RunUntilIdle();
230   EXPECT_TRUE(base::PathExists(file_path));
231 }
232 
TEST_F(DownloadFeedbackServiceTest,MultiplePendingFeedbackComplete)233 TEST_F(DownloadFeedbackServiceTest, MultiplePendingFeedbackComplete) {
234   const std::string ping_request = "ping";
235   const std::string ping_response = "resp";
236   const size_t num_downloads = 3;
237 
238   content::DownloadItem::AcquireFileCallback
239       download_discarded_callback[num_downloads];
240 
241   base::FilePath file_path[num_downloads];
242   content::MockDownloadItem item[num_downloads];
243   for (size_t i = 0; i < num_downloads; ++i) {
244     file_path[i] = CreateTestFile(i);
245     EXPECT_CALL(item[i], GetDangerType())
246         .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT));
247     EXPECT_CALL(item[i], GetReceivedBytes()).WillRepeatedly(Return(1000));
248     EXPECT_CALL(item[i], StealDangerousDownload(_))
249         .WillOnce(SaveArg<0>(&download_discarded_callback[i]));
250     DownloadFeedbackService::MaybeStorePingsForDownload(
251         DownloadProtectionService::UNCOMMON, &item[i], ping_request,
252         ping_response);
253     ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item[i]));
254   }
255 
256   {
257     DownloadFeedbackService service(request_context_getter_.get(),
258                                     file_task_runner_.get());
259     for (size_t i = 0; i < num_downloads; ++i) {
260       SCOPED_TRACE(i);
261       service.BeginFeedbackForDownload(&item[i]);
262       ASSERT_FALSE(download_discarded_callback[i].is_null());
263     }
264     EXPECT_EQ(0U, num_feedbacks());
265 
266     for (size_t i = 0; i < num_downloads; ++i) {
267       download_discarded_callback[i].Run(file_path[i]);
268     }
269 
270     ASSERT_EQ(3U, num_feedbacks());
271     EXPECT_TRUE(feedback(0)->start_called());
272     EXPECT_FALSE(feedback(1)->start_called());
273     EXPECT_FALSE(feedback(2)->start_called());
274 
275     feedback(0)->finish_callback().Run();
276 
277     EXPECT_FALSE(feedback(0));
278     EXPECT_TRUE(feedback(1)->start_called());
279     EXPECT_FALSE(feedback(2)->start_called());
280 
281     feedback(1)->finish_callback().Run();
282 
283     EXPECT_FALSE(feedback(0));
284     EXPECT_FALSE(feedback(1));
285     EXPECT_TRUE(feedback(2)->start_called());
286 
287     feedback(2)->finish_callback().Run();
288 
289     EXPECT_FALSE(feedback(0));
290     EXPECT_FALSE(feedback(1));
291     EXPECT_FALSE(feedback(2));
292   }
293 
294   base::RunLoop().RunUntilIdle();
295   // These files should still exist since the FakeDownloadFeedback does not
296   // delete them.
297   EXPECT_TRUE(base::PathExists(file_path[0]));
298   EXPECT_TRUE(base::PathExists(file_path[1]));
299   EXPECT_TRUE(base::PathExists(file_path[2]));
300 }
301 
TEST_F(DownloadFeedbackServiceTest,MultiFeedbackWithIncomplete)302 TEST_F(DownloadFeedbackServiceTest, MultiFeedbackWithIncomplete) {
303   const std::string ping_request = "ping";
304   const std::string ping_response = "resp";
305   const size_t num_downloads = 3;
306 
307   content::DownloadItem::AcquireFileCallback
308       download_discarded_callback[num_downloads];
309 
310   base::FilePath file_path[num_downloads];
311   content::MockDownloadItem item[num_downloads];
312   for (size_t i = 0; i < num_downloads; ++i) {
313     file_path[i] = CreateTestFile(i);
314     EXPECT_CALL(item[i], GetDangerType())
315         .WillRepeatedly(Return(content::DOWNLOAD_DANGER_TYPE_UNCOMMON_CONTENT));
316     EXPECT_CALL(item[i], GetReceivedBytes()).WillRepeatedly(Return(1000));
317     EXPECT_CALL(item[i], StealDangerousDownload(_))
318         .WillOnce(SaveArg<0>(&download_discarded_callback[i]));
319     DownloadFeedbackService::MaybeStorePingsForDownload(
320         DownloadProtectionService::UNCOMMON, &item[i], ping_request,
321         ping_response);
322     ASSERT_TRUE(DownloadFeedbackService::IsEnabledForDownload(item[i]));
323   }
324 
325   {
326     DownloadFeedbackService service(request_context_getter_.get(),
327                                     file_task_runner_.get());
328     for (size_t i = 0; i < num_downloads; ++i) {
329       SCOPED_TRACE(i);
330       service.BeginFeedbackForDownload(&item[i]);
331       ASSERT_FALSE(download_discarded_callback[i].is_null());
332     }
333     EXPECT_EQ(0U, num_feedbacks());
334 
335     download_discarded_callback[0].Run(file_path[0]);
336     ASSERT_EQ(1U, num_feedbacks());
337     ASSERT_TRUE(feedback(0));
338     EXPECT_TRUE(feedback(0)->start_called());
339 
340     download_discarded_callback[1].Run(file_path[1]);
341     ASSERT_EQ(2U, num_feedbacks());
342     ASSERT_TRUE(feedback(1));
343     EXPECT_FALSE(feedback(1)->start_called());
344 
345     feedback(0)->finish_callback().Run();
346     EXPECT_FALSE(feedback(0));
347     EXPECT_TRUE(feedback(1)->start_called());
348   }
349 
350   EXPECT_EQ(2U, num_feedbacks());
351   for (size_t i = 0; i < num_feedbacks(); ++i) {
352     SCOPED_TRACE(i);
353     EXPECT_FALSE(feedback(i));
354   }
355 
356   // Running a download acquired callback after the DownloadFeedbackService is
357   // destroyed should delete the file.
358   download_discarded_callback[2].Run(file_path[2]);
359   EXPECT_EQ(2U, num_feedbacks());
360 
361   // File should still exist since the FileUtilProxy task hasn't run yet.
362   EXPECT_TRUE(base::PathExists(file_path[2]));
363 
364   base::RunLoop().RunUntilIdle();
365   // File should be deleted since the AcquireFileCallback ran after the service
366   // was deleted.
367   EXPECT_FALSE(base::PathExists(file_path[2]));
368 
369   // These files should still exist since the FakeDownloadFeedback does not
370   // delete them.
371   EXPECT_TRUE(base::PathExists(file_path[0]));
372   EXPECT_TRUE(base::PathExists(file_path[1]));
373 }
374 
375 }  // namespace safe_browsing
376