OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/safe_browsing/download_protection_service.h" | 5 #include "chrome/browser/safe_browsing/download_protection_service.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback.h" | 11 #include "base/callback.h" |
| 12 #include "base/file_path.h" |
12 #include "base/memory/ref_counted.h" | 13 #include "base/memory/ref_counted.h" |
13 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
14 #include "base/message_loop.h" | 15 #include "base/message_loop.h" |
15 #include "chrome/common/safe_browsing/csd.pb.h" | 16 #include "chrome/common/safe_browsing/csd.pb.h" |
16 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | 17 #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
17 #include "content/browser/browser_thread.h" | 18 #include "content/browser/browser_thread.h" |
| 19 #include "content/browser/download/download_item.h" |
| 20 #include "content/public/common/url_fetcher_delegate.h" |
18 #include "content/test/test_url_fetcher_factory.h" | 21 #include "content/test/test_url_fetcher_factory.h" |
19 #include "googleurl/src/gurl.h" | 22 #include "googleurl/src/gurl.h" |
20 #include "testing/gmock/include/gmock/gmock.h" | 23 #include "testing/gmock/include/gmock/gmock.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
22 | 25 |
23 using ::testing::Return; | 26 using ::testing::Return; |
24 using ::testing::_; | 27 using ::testing::_; |
25 | 28 |
26 namespace safe_browsing { | 29 namespace safe_browsing { |
27 namespace { | 30 namespace { |
28 class MockSafeBrowsingService : public SafeBrowsingService { | 31 class MockSafeBrowsingService : public SafeBrowsingService { |
29 public: | 32 public: |
30 MockSafeBrowsingService() {} | 33 MockSafeBrowsingService() {} |
31 virtual ~MockSafeBrowsingService() {} | 34 virtual ~MockSafeBrowsingService() {} |
32 | 35 |
33 MOCK_METHOD1(MatchDownloadWhitelistUrl, bool(const GURL&)); | 36 MOCK_METHOD1(MatchDownloadWhitelistUrl, bool(const GURL&)); |
34 | 37 |
35 private: | 38 private: |
36 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService); | 39 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService); |
37 }; | 40 }; |
38 } // namespace | 41 } // namespace |
39 | 42 |
40 class DownloadProtectionServiceTest : public testing::Test { | 43 class DownloadProtectionServiceTest : public testing::Test { |
41 protected: | 44 protected: |
42 virtual void SetUp() { | 45 virtual void SetUp() { |
43 ui_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_)); | 46 ui_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_)); |
44 io_thread_.reset(new BrowserThread(BrowserThread::IO, &msg_loop_)); | 47 // Start real threads for the IO and File threads so that the DCHECKs |
| 48 // to test that we're on the correct thread work. |
| 49 io_thread_.reset(new BrowserThread(BrowserThread::IO)); |
| 50 ASSERT_TRUE(io_thread_->Start()); |
| 51 file_thread_.reset(new BrowserThread(BrowserThread::FILE)); |
| 52 ASSERT_TRUE(file_thread_->Start()); |
45 sb_service_ = new MockSafeBrowsingService(); | 53 sb_service_ = new MockSafeBrowsingService(); |
46 download_service_ = new DownloadProtectionService(sb_service_.get(), | 54 download_service_ = sb_service_->download_protection_service(); |
47 NULL); | |
48 download_service_->SetEnabled(true); | 55 download_service_->SetEnabled(true); |
49 msg_loop_.RunAllPending(); | 56 msg_loop_.RunAllPending(); |
50 } | 57 } |
51 | 58 |
52 virtual void TearDown() { | 59 virtual void TearDown() { |
53 msg_loop_.RunAllPending(); | 60 // Flush all of the thread message loops to ensure that there are no |
54 download_service_ = NULL; | 61 // tasks currently running. |
| 62 FlushThreadMessageLoops(); |
55 sb_service_ = NULL; | 63 sb_service_ = NULL; |
56 io_thread_.reset(); | 64 io_thread_.reset(); |
| 65 file_thread_.reset(); |
57 ui_thread_.reset(); | 66 ui_thread_.reset(); |
58 } | 67 } |
59 | 68 |
60 bool RequestContainsResource(const ClientDownloadRequest& request, | 69 bool RequestContainsResource(const ClientDownloadRequest& request, |
61 ClientDownloadRequest::ResourceType type, | 70 ClientDownloadRequest::ResourceType type, |
62 const std::string& url, | 71 const std::string& url, |
63 const std::string& referrer) { | 72 const std::string& referrer) { |
64 for (int i = 0; i < request.resources_size(); ++i) { | 73 for (int i = 0; i < request.resources_size(); ++i) { |
65 if (request.resources(i).url() == url && | 74 if (request.resources(i).url() == url && |
66 request.resources(i).type() == type && | 75 request.resources(i).type() == type && |
67 (referrer.empty() || request.resources(i).referrer() == referrer)) { | 76 (referrer.empty() || request.resources(i).referrer() == referrer)) { |
68 return true; | 77 return true; |
69 } | 78 } |
70 } | 79 } |
71 return false; | 80 return false; |
72 } | 81 } |
73 | 82 |
| 83 // Flushes any pending tasks in the message loops of all threads. |
| 84 void FlushThreadMessageLoops() { |
| 85 FlushMessageLoop(BrowserThread::FILE); |
| 86 FlushMessageLoop(BrowserThread::IO); |
| 87 msg_loop_.RunAllPending(); |
| 88 } |
| 89 |
| 90 private: |
| 91 // Helper functions for FlushThreadMessageLoops. |
| 92 void RunAllPendingAndQuitUI() { |
| 93 MessageLoop::current()->RunAllPending(); |
| 94 BrowserThread::PostTask( |
| 95 BrowserThread::UI, |
| 96 FROM_HERE, |
| 97 base::Bind(&DownloadProtectionServiceTest::QuitMessageLoop, |
| 98 base::Unretained(this))); |
| 99 } |
| 100 |
| 101 void QuitMessageLoop() { |
| 102 MessageLoop::current()->Quit(); |
| 103 } |
| 104 |
| 105 void PostRunMessageLoopTask(BrowserThread::ID thread) { |
| 106 BrowserThread::PostTask( |
| 107 thread, |
| 108 FROM_HERE, |
| 109 base::Bind(&DownloadProtectionServiceTest::RunAllPendingAndQuitUI, |
| 110 base::Unretained(this))); |
| 111 } |
| 112 |
| 113 void FlushMessageLoop(BrowserThread::ID thread) { |
| 114 BrowserThread::PostTask( |
| 115 BrowserThread::UI, |
| 116 FROM_HERE, |
| 117 base::Bind(&DownloadProtectionServiceTest::PostRunMessageLoopTask, |
| 118 base::Unretained(this), thread)); |
| 119 msg_loop_.Run(); |
| 120 } |
| 121 |
74 public: | 122 public: |
75 void CheckDoneCallback( | 123 void CheckDoneCallback( |
76 DownloadProtectionService::DownloadCheckResult result) { | 124 DownloadProtectionService::DownloadCheckResult result) { |
77 result_ = result; | 125 result_ = result; |
78 msg_loop_.Quit(); | 126 msg_loop_.Quit(); |
79 } | 127 } |
80 | 128 |
| 129 void SendURLFetchComplete(TestURLFetcher* fetcher) { |
| 130 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 131 } |
| 132 |
81 protected: | 133 protected: |
82 scoped_refptr<MockSafeBrowsingService> sb_service_; | 134 scoped_refptr<MockSafeBrowsingService> sb_service_; |
83 scoped_refptr<DownloadProtectionService> download_service_; | 135 DownloadProtectionService* download_service_; |
84 MessageLoop msg_loop_; | 136 MessageLoop msg_loop_; |
85 DownloadProtectionService::DownloadCheckResult result_; | 137 DownloadProtectionService::DownloadCheckResult result_; |
86 scoped_ptr<BrowserThread> io_thread_; | 138 scoped_ptr<BrowserThread> io_thread_; |
| 139 scoped_ptr<BrowserThread> file_thread_; |
87 scoped_ptr<BrowserThread> ui_thread_; | 140 scoped_ptr<BrowserThread> ui_thread_; |
88 }; | 141 }; |
89 | 142 |
90 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadInvalidUrl) { | 143 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadInvalidUrl) { |
91 DownloadProtectionService::DownloadInfo info; | 144 DownloadProtectionService::DownloadInfo info; |
92 EXPECT_TRUE(download_service_->CheckClientDownload( | 145 download_service_->CheckClientDownload( |
93 info, | 146 info, |
94 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 147 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
95 base::Unretained(this)))); | 148 base::Unretained(this))); |
| 149 msg_loop_.Run(); |
| 150 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
| 151 |
96 // Only http is supported for now. | 152 // Only http is supported for now. |
| 153 info.local_file = FilePath(FILE_PATH_LITERAL("a.exe")); |
97 info.download_url_chain.push_back(GURL("https://www.google.com/")); | 154 info.download_url_chain.push_back(GURL("https://www.google.com/")); |
98 EXPECT_TRUE(download_service_->CheckClientDownload( | 155 download_service_->CheckClientDownload( |
99 info, | 156 info, |
100 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 157 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
101 base::Unretained(this)))); | 158 base::Unretained(this))); |
| 159 msg_loop_.Run(); |
| 160 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
| 161 |
102 info.download_url_chain[0] = GURL("ftp://www.google.com/"); | 162 info.download_url_chain[0] = GURL("ftp://www.google.com/"); |
103 EXPECT_TRUE(download_service_->CheckClientDownload( | 163 download_service_->CheckClientDownload( |
104 info, | 164 info, |
105 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 165 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
106 base::Unretained(this)))); | 166 base::Unretained(this))); |
| 167 msg_loop_.Run(); |
| 168 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
107 } | 169 } |
108 | 170 |
109 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadWhitelistedUrl) { | 171 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadWhitelistedUrl) { |
110 DownloadProtectionService::DownloadInfo info; | 172 DownloadProtectionService::DownloadInfo info; |
| 173 info.local_file = FilePath(FILE_PATH_LITERAL("a.exe")); |
111 info.download_url_chain.push_back(GURL("http://www.evil.com/bla.exe")); | 174 info.download_url_chain.push_back(GURL("http://www.evil.com/bla.exe")); |
112 info.download_url_chain.push_back(GURL("http://www.google.com/a.exe")); | 175 info.download_url_chain.push_back(GURL("http://www.google.com/a.exe")); |
113 info.referrer_url = GURL("http://www.google.com/"); | 176 info.referrer_url = GURL("http://www.google.com/"); |
114 | 177 |
115 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) | 178 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) |
116 .WillRepeatedly(Return(false)); | 179 .WillRepeatedly(Return(false)); |
117 EXPECT_CALL(*sb_service_, | 180 EXPECT_CALL(*sb_service_, |
118 MatchDownloadWhitelistUrl(GURL("http://www.google.com/a.exe"))) | 181 MatchDownloadWhitelistUrl(GURL("http://www.google.com/a.exe"))) |
119 .WillRepeatedly(Return(true)); | 182 .WillRepeatedly(Return(true)); |
120 | 183 |
121 EXPECT_FALSE(download_service_->CheckClientDownload( | 184 download_service_->CheckClientDownload( |
122 info, | 185 info, |
123 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 186 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
124 base::Unretained(this)))); | 187 base::Unretained(this))); |
125 msg_loop_.Run(); | 188 msg_loop_.Run(); |
126 EXPECT_EQ(DownloadProtectionService::SAFE, result_); | 189 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
127 | 190 |
128 // Check that the referrer is matched against the whitelist. | 191 // Check that the referrer is matched against the whitelist. |
129 info.download_url_chain.pop_back(); | 192 info.download_url_chain.pop_back(); |
130 info.referrer_url = GURL("http://www.google.com/a.exe"); | 193 info.referrer_url = GURL("http://www.google.com/a.exe"); |
131 EXPECT_FALSE(download_service_->CheckClientDownload( | 194 download_service_->CheckClientDownload( |
132 info, | 195 info, |
133 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 196 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
134 base::Unretained(this)))); | 197 base::Unretained(this))); |
135 msg_loop_.Run(); | 198 msg_loop_.Run(); |
136 EXPECT_EQ(DownloadProtectionService::SAFE, result_); | 199 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
137 } | 200 } |
138 | 201 |
139 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadFetchFailed) { | 202 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadFetchFailed) { |
140 FakeURLFetcherFactory factory; | 203 FakeURLFetcherFactory factory; |
141 // HTTP request will fail. | 204 // HTTP request will fail. |
142 factory.SetFakeResponse( | 205 factory.SetFakeResponse( |
143 DownloadProtectionService::kDownloadRequestUrl, "", false); | 206 DownloadProtectionService::kDownloadRequestUrl, "", false); |
144 | 207 |
145 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) | 208 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) |
146 .WillRepeatedly(Return(false)); | 209 .WillRepeatedly(Return(false)); |
147 | 210 |
148 DownloadProtectionService::DownloadInfo info; | 211 DownloadProtectionService::DownloadInfo info; |
| 212 info.local_file = FilePath(FILE_PATH_LITERAL("a.exe")); |
149 info.download_url_chain.push_back(GURL("http://www.evil.com/a.exe")); | 213 info.download_url_chain.push_back(GURL("http://www.evil.com/a.exe")); |
150 info.referrer_url = GURL("http://www.google.com/"); | 214 info.referrer_url = GURL("http://www.google.com/"); |
151 EXPECT_FALSE(download_service_->CheckClientDownload( | 215 download_service_->CheckClientDownload( |
152 info, | 216 info, |
153 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 217 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
154 base::Unretained(this)))); | 218 base::Unretained(this))); |
155 msg_loop_.Run(); | 219 msg_loop_.Run(); |
156 EXPECT_EQ(DownloadProtectionService::SAFE, result_); | 220 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
157 } | 221 } |
158 | 222 |
159 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadSuccess) { | 223 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadSuccess) { |
160 FakeURLFetcherFactory factory; | 224 FakeURLFetcherFactory factory; |
161 // Empty response means SAFE. | 225 // Empty response means SAFE. |
162 factory.SetFakeResponse( | 226 factory.SetFakeResponse( |
163 DownloadProtectionService::kDownloadRequestUrl, "", true); | 227 DownloadProtectionService::kDownloadRequestUrl, "", true); |
164 | 228 |
165 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) | 229 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) |
166 .WillRepeatedly(Return(false)); | 230 .WillRepeatedly(Return(false)); |
167 | 231 |
168 DownloadProtectionService::DownloadInfo info; | 232 DownloadProtectionService::DownloadInfo info; |
| 233 info.local_file = FilePath(FILE_PATH_LITERAL("a.exe")); |
169 info.download_url_chain.push_back(GURL("http://www.evil.com/a.exe")); | 234 info.download_url_chain.push_back(GURL("http://www.evil.com/a.exe")); |
170 info.referrer_url = GURL("http://www.google.com/"); | 235 info.referrer_url = GURL("http://www.google.com/"); |
171 EXPECT_FALSE(download_service_->CheckClientDownload( | 236 download_service_->CheckClientDownload( |
172 info, | 237 info, |
173 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 238 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
174 base::Unretained(this)))); | 239 base::Unretained(this))); |
175 msg_loop_.Run(); | 240 msg_loop_.Run(); |
176 EXPECT_EQ(DownloadProtectionService::SAFE, result_); | 241 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
177 | 242 |
178 // Invalid response should be safe too. | 243 // Invalid response should be safe too. |
179 factory.SetFakeResponse( | 244 factory.SetFakeResponse( |
180 DownloadProtectionService::kDownloadRequestUrl, "bla", true); | 245 DownloadProtectionService::kDownloadRequestUrl, "bla", true); |
181 | 246 |
182 EXPECT_FALSE(download_service_->CheckClientDownload( | 247 download_service_->CheckClientDownload( |
183 info, | 248 info, |
184 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 249 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
185 base::Unretained(this)))); | 250 base::Unretained(this))); |
186 msg_loop_.Run(); | 251 msg_loop_.Run(); |
187 EXPECT_EQ(DownloadProtectionService::SAFE, result_); | 252 EXPECT_EQ(DownloadProtectionService::SAFE, result_); |
188 } | 253 } |
189 | 254 |
190 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequest) { | 255 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequest) { |
191 TestURLFetcherFactory factory; | 256 TestURLFetcherFactory factory; |
192 | 257 |
193 DownloadProtectionService::DownloadInfo info; | 258 DownloadProtectionService::DownloadInfo info; |
| 259 info.local_file = FilePath(FILE_PATH_LITERAL("bla.exe")); |
194 info.download_url_chain.push_back(GURL("http://www.google.com/")); | 260 info.download_url_chain.push_back(GURL("http://www.google.com/")); |
195 info.download_url_chain.push_back(GURL("http://www.google.com/bla.exe")); | 261 info.download_url_chain.push_back(GURL("http://www.google.com/bla.exe")); |
196 info.referrer_url = GURL("http://www.google.com/"); | 262 info.referrer_url = GURL("http://www.google.com/"); |
197 info.sha256_hash = "hash"; | 263 info.sha256_hash = "hash"; |
198 info.total_bytes = 100; | 264 info.total_bytes = 100; |
199 info.user_initiated = false; | 265 info.user_initiated = false; |
200 | 266 |
201 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) | 267 EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) |
202 .WillRepeatedly(Return(false)); | 268 .WillRepeatedly(Return(false)); |
203 EXPECT_FALSE(download_service_->CheckClientDownload( | 269 download_service_->CheckClientDownload( |
204 info, | 270 info, |
205 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, | 271 base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, |
206 base::Unretained(this)))); | 272 base::Unretained(this))); |
207 msg_loop_.RunAllPending(); // Wait until StartCheckClientDownload is called. | 273 // Run the message loop(s) until SendRequest is called. |
| 274 FlushThreadMessageLoops(); |
208 | 275 |
209 ASSERT_TRUE(factory.GetFetcherByID(0)); | 276 TestURLFetcher* fetcher = factory.GetFetcherByID(0); |
| 277 ASSERT_TRUE(fetcher); |
210 ClientDownloadRequest request; | 278 ClientDownloadRequest request; |
211 EXPECT_TRUE(request.ParseFromString( | 279 EXPECT_TRUE(request.ParseFromString(fetcher->upload_data())); |
212 factory.GetFetcherByID(0)->upload_data())); | |
213 EXPECT_EQ("http://www.google.com/bla.exe", request.url()); | 280 EXPECT_EQ("http://www.google.com/bla.exe", request.url()); |
214 EXPECT_EQ(info.sha256_hash, request.digests().sha256()); | 281 EXPECT_EQ(info.sha256_hash, request.digests().sha256()); |
215 EXPECT_EQ(info.total_bytes, request.length()); | 282 EXPECT_EQ(info.total_bytes, request.length()); |
216 EXPECT_EQ(info.user_initiated, request.user_initiated()); | 283 EXPECT_EQ(info.user_initiated, request.user_initiated()); |
217 EXPECT_EQ(2, request.resources_size()); | 284 EXPECT_EQ(2, request.resources_size()); |
218 EXPECT_TRUE(RequestContainsResource(request, | 285 EXPECT_TRUE(RequestContainsResource(request, |
219 ClientDownloadRequest::DOWNLOAD_REDIRECT, | 286 ClientDownloadRequest::DOWNLOAD_REDIRECT, |
220 "http://www.google.com/", "")); | 287 "http://www.google.com/", "")); |
221 EXPECT_TRUE(RequestContainsResource(request, | 288 EXPECT_TRUE(RequestContainsResource(request, |
222 ClientDownloadRequest::DOWNLOAD_URL, | 289 ClientDownloadRequest::DOWNLOAD_URL, |
223 "http://www.google.com/bla.exe", | 290 "http://www.google.com/bla.exe", |
224 info.referrer_url.spec())); | 291 info.referrer_url.spec())); |
| 292 |
| 293 // Simulate the request finishing. |
| 294 MessageLoop::current()->PostTask( |
| 295 FROM_HERE, |
| 296 base::Bind(&DownloadProtectionServiceTest::SendURLFetchComplete, |
| 297 base::Unretained(this), fetcher)); |
| 298 msg_loop_.Run(); |
225 } | 299 } |
226 } // namespace safe_browsing | 300 } // namespace safe_browsing |
OLD | NEW |