OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 <vector> | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/strings/stringprintf.h" | |
10 #include "base/time/time.h" | |
11 #include "chrome/browser/safe_browsing/v4_protocol_manager.h" | |
12 #include "components/safe_browsing_db/safebrowsing.pb.h" | |
13 #include "components/safe_browsing_db/util.h" | |
14 #include "net/base/escape.h" | |
15 #include "net/base/load_flags.h" | |
16 #include "net/base/net_errors.h" | |
17 #include "net/url_request/test_url_fetcher_factory.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 using base::Time; | |
21 using base::TimeDelta; | |
22 | |
23 namespace { | |
24 | |
25 const char kClient[] = "unittest"; | |
26 const char kAppVer[] = "1.0"; | |
27 const char kKeyParam[] = "test_key_param"; | |
28 | |
29 } // namespace | |
30 | |
31 namespace safe_browsing { | |
32 | |
33 class SafeBrowsingV4ProtocolManagerTest : public testing::Test { | |
34 protected: | |
35 | |
36 scoped_ptr<V4ProtocolManager> CreateProtocolManager() { | |
37 V4ProtocolConfig config; | |
38 config.client_name = kClient; | |
39 config.version = kAppVer; | |
40 config.key_param = kKeyParam; | |
41 return scoped_ptr<V4ProtocolManager>( | |
42 V4ProtocolManager::Create(NULL, config)); | |
43 } | |
44 | |
45 std::string GetStockV4HashResponse() { | |
46 FindFullHashesResponse res; | |
47 res.mutable_negative_cache_duration()->set_seconds(600); | |
48 ThreatMatch* m = res.add_matches(); | |
49 m->set_threat_type(API_ABUSE); | |
50 m->set_platform_type(CHROME_PLATFORM); | |
51 m->set_threat_entry_type(URL_EXPRESSION); | |
52 m->mutable_cache_duration()->set_seconds(300); | |
53 m->mutable_threat()->set_hash( | |
54 SBFullHashToString(SBFullHashForString("Everything's shiny, Cap'n."))); | |
55 ThreatEntryMetadata::MetadataEntry* e = | |
56 m->mutable_threat_entry_metadata()->add_entries(); | |
57 e->set_key("permission"); | |
58 e->set_value("NOTIFICATIONS"); | |
59 | |
60 // Serialize. | |
61 std::string res_data; | |
62 res.SerializeToString(&res_data); | |
63 | |
64 return res_data; | |
65 } | |
66 }; | |
67 | |
68 void ValidateGetV4HashResults( | |
69 const std::vector<SBFullHashResult>& expected_full_hashes, | |
70 const base::TimeDelta& expected_cache_duration, | |
71 const std::vector<SBFullHashResult>& full_hashes, | |
72 const base::TimeDelta& cache_duration) { | |
73 EXPECT_EQ(expected_cache_duration, cache_duration); | |
74 ASSERT_EQ(expected_full_hashes.size(), full_hashes.size()); | |
75 | |
76 for (unsigned int i = 0; i < expected_full_hashes.size(); ++i) { | |
77 const SBFullHashResult& expected = expected_full_hashes[i]; | |
78 const SBFullHashResult& actual = full_hashes[i]; | |
79 EXPECT_TRUE(SBFullHashEqual(expected.hash, actual.hash)); | |
80 EXPECT_EQ(expected.metadata, actual.metadata); | |
81 EXPECT_EQ(expected.cache_duration, actual.cache_duration); | |
82 } | |
83 } | |
84 | |
85 TEST_F(SafeBrowsingV4ProtocolManagerTest, TestGetHashErrorHandlingNetwork) { | |
86 net::TestURLFetcherFactory factory; | |
87 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
88 | |
89 std::vector<SBPrefix> prefixes; | |
90 std::vector<SBFullHashResult> expected_full_hashes; | |
91 base::TimeDelta expected_cache_duration; | |
92 | |
93 pm->GetFullHashesWithApis( | |
94 prefixes, base::Bind(&ValidateGetV4HashResults, expected_full_hashes, | |
95 expected_cache_duration)); | |
96 | |
97 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); | |
98 DCHECK(fetcher); | |
99 // Failed request status should result in error. | |
100 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
101 net::ERR_CONNECTION_RESET)); | |
102 fetcher->set_response_code(200); | |
103 fetcher->SetResponseString(GetStockV4HashResponse()); | |
104 fetcher->delegate()->OnURLFetchComplete(fetcher); | |
105 | |
106 // Should have recorded one error, but back off multiplier is unchanged. | |
107 EXPECT_EQ(1ul, pm->gethash_error_count_); | |
108 EXPECT_EQ(1ul, pm->gethash_back_off_mult_); | |
109 } | |
110 | |
111 TEST_F(SafeBrowsingV4ProtocolManagerTest, | |
112 TestGetHashErrorHandlingResponseCode) { | |
113 net::TestURLFetcherFactory factory; | |
114 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
115 | |
116 std::vector<SBPrefix> prefixes; | |
117 std::vector<SBFullHashResult> expected_full_hashes; | |
118 base::TimeDelta expected_cache_duration; | |
119 | |
120 pm->GetFullHashesWithApis( | |
121 prefixes, base::Bind(&ValidateGetV4HashResults, expected_full_hashes, | |
122 expected_cache_duration)); | |
123 | |
124 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); | |
125 DCHECK(fetcher); | |
126 fetcher->set_status(net::URLRequestStatus()); | |
127 // Response code of anything other than 200 should result in error. | |
128 fetcher->set_response_code(204); | |
129 fetcher->SetResponseString(GetStockV4HashResponse()); | |
130 fetcher->delegate()->OnURLFetchComplete(fetcher); | |
131 | |
132 // Should have recorded one error, but back off multiplier is unchanged. | |
133 EXPECT_EQ(1ul, pm->gethash_error_count_); | |
134 EXPECT_EQ(1ul, pm->gethash_back_off_mult_); | |
135 } | |
136 | |
137 TEST_F(SafeBrowsingV4ProtocolManagerTest, TestGetHashErrorHandlingOK) { | |
138 net::TestURLFetcherFactory factory; | |
139 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
140 | |
141 std::vector<SBPrefix> prefixes; | |
142 std::vector<SBFullHashResult> expected_full_hashes; | |
143 SBFullHashResult hash_result; | |
144 hash_result.hash = SBFullHashForString("Everything's shiny, Cap'n."); | |
145 hash_result.metadata = "NOTIFICATIONS,"; | |
146 hash_result.cache_duration = base::TimeDelta::FromSeconds(300); | |
147 expected_full_hashes.push_back(hash_result); | |
148 base::TimeDelta expected_cache_duration = base::TimeDelta::FromSeconds(600); | |
149 | |
150 pm->GetFullHashesWithApis( | |
151 prefixes, base::Bind(&ValidateGetV4HashResults, expected_full_hashes, | |
152 expected_cache_duration)); | |
153 | |
154 net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); | |
155 DCHECK(fetcher); | |
156 fetcher->set_status(net::URLRequestStatus()); | |
157 fetcher->set_response_code(200); | |
158 fetcher->SetResponseString(GetStockV4HashResponse()); | |
159 fetcher->delegate()->OnURLFetchComplete(fetcher); | |
160 | |
161 // No error, back off multiplier is unchanged. | |
162 EXPECT_EQ(0ul, pm->gethash_error_count_); | |
163 EXPECT_EQ(1ul, pm->gethash_back_off_mult_); | |
164 } | |
165 | |
166 TEST_F(SafeBrowsingV4ProtocolManagerTest, TestGetHashBackOffTimes) { | |
167 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
168 | |
169 // No errors or back off time yet. | |
170 EXPECT_EQ(0U, pm->gethash_error_count_); | |
171 EXPECT_EQ(1U, pm->gethash_back_off_mult_); | |
172 Time now = Time::Now(); | |
173 EXPECT_TRUE(pm->next_gethash_time_ < now); | |
174 | |
175 // 1 error. | |
176 pm->HandleGetHashError(now); | |
177 EXPECT_EQ(1U, pm->gethash_error_count_); | |
178 EXPECT_EQ(1U, pm->gethash_back_off_mult_); | |
179 EXPECT_LE(now + TimeDelta::FromMinutes(15), pm->next_gethash_time_); | |
180 EXPECT_GE(now + TimeDelta::FromMinutes(30), pm->next_gethash_time_); | |
181 | |
182 // 2 errors. | |
183 pm->HandleGetHashError(now); | |
184 EXPECT_EQ(2U, pm->gethash_error_count_); | |
185 EXPECT_EQ(2U, pm->gethash_back_off_mult_); | |
186 EXPECT_LE(now + TimeDelta::FromMinutes(30), pm->next_gethash_time_); | |
187 EXPECT_GE(now + TimeDelta::FromMinutes(60), pm->next_gethash_time_); | |
188 | |
189 // 3 errors. | |
190 pm->HandleGetHashError(now); | |
191 EXPECT_EQ(3U, pm->gethash_error_count_); | |
192 EXPECT_EQ(4U, pm->gethash_back_off_mult_); | |
193 EXPECT_LE(now + TimeDelta::FromMinutes(60), pm->next_gethash_time_); | |
194 EXPECT_GE(now + TimeDelta::FromMinutes(120), pm->next_gethash_time_); | |
195 | |
196 // 4 errors. | |
197 pm->HandleGetHashError(now); | |
198 EXPECT_EQ(4U, pm->gethash_error_count_); | |
199 EXPECT_EQ(8U, pm->gethash_back_off_mult_); | |
200 EXPECT_LE(now + TimeDelta::FromMinutes(120), pm->next_gethash_time_); | |
201 EXPECT_GE(now + TimeDelta::FromMinutes(240), pm->next_gethash_time_); | |
202 | |
203 // 5 errors. | |
204 pm->HandleGetHashError(now); | |
205 EXPECT_EQ(5U, pm->gethash_error_count_); | |
206 EXPECT_EQ(16U, pm->gethash_back_off_mult_); | |
207 EXPECT_LE(now + TimeDelta::FromMinutes(240), pm->next_gethash_time_); | |
208 EXPECT_GE(now + TimeDelta::FromMinutes(480), pm->next_gethash_time_); | |
209 | |
210 // 6 errors. | |
211 pm->HandleGetHashError(now); | |
212 EXPECT_EQ(6U, pm->gethash_error_count_); | |
213 EXPECT_EQ(32U, pm->gethash_back_off_mult_); | |
214 EXPECT_LE(now + TimeDelta::FromMinutes(480), pm->next_gethash_time_); | |
215 EXPECT_GE(now + TimeDelta::FromMinutes(960), pm->next_gethash_time_); | |
216 | |
217 // 7 errors. | |
218 pm->HandleGetHashError(now); | |
219 EXPECT_EQ(7U, pm->gethash_error_count_); | |
220 EXPECT_EQ(64U, pm->gethash_back_off_mult_); | |
221 EXPECT_LE(now + TimeDelta::FromMinutes(960), pm->next_gethash_time_); | |
222 EXPECT_GE(now + TimeDelta::FromMinutes(1920), pm->next_gethash_time_); | |
223 | |
224 // 8 errors, reached max backoff. | |
225 pm->HandleGetHashError(now); | |
226 EXPECT_EQ(8U, pm->gethash_error_count_); | |
227 EXPECT_EQ(128U, pm->gethash_back_off_mult_); | |
228 EXPECT_EQ(now + TimeDelta::FromHours(24), pm->next_gethash_time_); | |
229 | |
230 // 9 errors, reached max backoff and multiplier capped. | |
231 pm->HandleGetHashError(now); | |
232 EXPECT_EQ(9U, pm->gethash_error_count_); | |
233 EXPECT_EQ(128U, pm->gethash_back_off_mult_); | |
234 EXPECT_EQ(now + TimeDelta::FromHours(24), pm->next_gethash_time_); | |
235 } | |
236 | |
237 TEST_F(SafeBrowsingV4ProtocolManagerTest, TestGetHashUrl) { | |
238 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
239 | |
240 EXPECT_EQ( | |
241 "https://safebrowsing.googleapis.com/v4/encodedFullHashes/request_base64?" | |
242 "alt=proto&client_id=unittest&client_version=1.0&key=test_key_param", | |
243 pm->GetHashUrl("request_base64").spec()); | |
244 } | |
245 | |
246 TEST_F(SafeBrowsingV4ProtocolManagerTest, TestGetHashRequest) { | |
247 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
248 | |
249 FindFullHashesRequest req; | |
250 ThreatInfo* info = req.mutable_threat_info(); | |
251 info->add_threat_types(API_ABUSE); | |
252 info->add_platform_types(CHROME_PLATFORM); | |
253 info->add_threat_entry_types(URL_EXPRESSION); | |
254 | |
255 SBPrefix one = 1u; | |
256 SBPrefix two = 2u; | |
257 SBPrefix three = 3u; | |
258 std::string hash(reinterpret_cast<const char*>(&one), sizeof(SBPrefix)); | |
259 info->add_threat_entries()->set_hash(hash); | |
260 hash.clear(); | |
261 hash.append(reinterpret_cast<const char*>(&two), sizeof(SBPrefix)); | |
262 info->add_threat_entries()->set_hash(hash); | |
263 hash.clear(); | |
264 hash.append(reinterpret_cast<const char*>(&three), sizeof(SBPrefix)); | |
265 info->add_threat_entries()->set_hash(hash); | |
266 | |
267 // Serialize and Base64 encode. | |
268 std::string req_data, req_base64; | |
269 req.SerializeToString(&req_data); | |
270 base::Base64Encode(req_data, &req_base64); | |
271 | |
272 std::vector<PlatformType> platform; | |
273 platform.push_back(CHROME_PLATFORM); | |
274 std::vector<SBPrefix> prefixes; | |
275 prefixes.push_back(one); | |
276 prefixes.push_back(two); | |
277 prefixes.push_back(three); | |
278 EXPECT_EQ(req_base64, pm->GetHashRequest(prefixes, platform, API_ABUSE)); | |
279 } | |
280 | |
281 TEST_F(SafeBrowsingV4ProtocolManagerTest, TestParseHashResponse) { | |
282 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
283 | |
284 FindFullHashesResponse res; | |
285 res.mutable_negative_cache_duration()->set_seconds(600); | |
286 res.mutable_minimum_wait_duration()->set_seconds(400); | |
287 ThreatMatch* m = res.add_matches(); | |
288 m->set_threat_type(API_ABUSE); | |
289 m->set_platform_type(CHROME_PLATFORM); | |
290 m->set_threat_entry_type(URL_EXPRESSION); | |
291 m->mutable_cache_duration()->set_seconds(300); | |
292 m->mutable_threat()->set_hash( | |
293 SBFullHashToString(SBFullHashForString("Everything's shiny, Cap'n."))); | |
294 ThreatEntryMetadata::MetadataEntry* e = | |
295 m->mutable_threat_entry_metadata()->add_entries(); | |
296 e->set_key("permission"); | |
297 e->set_value("NOTIFICATIONS"); | |
298 | |
299 // Serialize. | |
300 std::string res_data; | |
301 res.SerializeToString(&res_data); | |
302 | |
303 Time now = Time::Now(); | |
304 std::vector<SBFullHashResult> full_hashes; | |
305 base::TimeDelta cache_lifetime; | |
306 EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hashes, &cache_lifetime)); | |
307 | |
308 EXPECT_EQ(base::TimeDelta::FromSeconds(600), cache_lifetime); | |
309 EXPECT_EQ(1ul, full_hashes.size()); | |
310 EXPECT_TRUE(SBFullHashEqual(SBFullHashForString("Everything's shiny, Cap'n."), | |
311 full_hashes[0].hash)); | |
312 EXPECT_EQ("NOTIFICATIONS,", full_hashes[0].metadata); | |
313 EXPECT_EQ(base::TimeDelta::FromSeconds(300), full_hashes[0].cache_duration); | |
314 EXPECT_LE(now + base::TimeDelta::FromSeconds(400), pm->next_gethash_time_); | |
315 } | |
316 | |
317 // Adds an entry with an ignored ThreatEntryType. | |
318 TEST_F(SafeBrowsingV4ProtocolManagerTest, | |
319 TestParseHashResponseWrongThreatEntryType) { | |
320 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
321 | |
322 FindFullHashesResponse res; | |
323 res.mutable_negative_cache_duration()->set_seconds(600); | |
324 res.add_matches()->set_threat_entry_type(BINARY_DIGEST); | |
325 | |
326 // Serialize. | |
327 std::string res_data; | |
328 res.SerializeToString(&res_data); | |
329 | |
330 std::vector<SBFullHashResult> full_hashes; | |
331 base::TimeDelta cache_lifetime; | |
332 EXPECT_FALSE(pm->ParseHashResponse(res_data, &full_hashes, &cache_lifetime)); | |
333 | |
334 EXPECT_EQ(base::TimeDelta::FromSeconds(600), cache_lifetime); | |
335 // There should be no hash results. | |
336 EXPECT_EQ(0ul, full_hashes.size()); | |
337 } | |
338 | |
339 // Adds an entry with a SOCIAL_ENGINEERING threat type. | |
340 TEST_F(SafeBrowsingV4ProtocolManagerTest, | |
341 TestParseHashResponseSocialEngineeringThreatType) { | |
342 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
343 | |
344 FindFullHashesResponse res; | |
345 res.mutable_negative_cache_duration()->set_seconds(600); | |
346 ThreatMatch* m = res.add_matches(); | |
347 m->set_threat_type(SOCIAL_ENGINEERING); | |
348 m->set_platform_type(CHROME_PLATFORM); | |
349 m->set_threat_entry_type(URL_EXPRESSION); | |
350 m->mutable_threat()->set_hash( | |
351 SBFullHashToString(SBFullHashForString("Not to fret."))); | |
352 ThreatEntryMetadata::MetadataEntry* e = | |
353 m->mutable_threat_entry_metadata()->add_entries(); | |
354 e->set_key("permission"); | |
355 e->set_value("IGNORED"); | |
356 | |
357 // Serialize. | |
358 std::string res_data; | |
359 res.SerializeToString(&res_data); | |
360 | |
361 std::vector<SBFullHashResult> full_hashes; | |
362 base::TimeDelta cache_lifetime; | |
363 EXPECT_FALSE(pm->ParseHashResponse(res_data, &full_hashes, &cache_lifetime)); | |
364 | |
365 EXPECT_EQ(base::TimeDelta::FromSeconds(600), cache_lifetime); | |
366 EXPECT_EQ(0ul, full_hashes.size()); | |
367 } | |
368 | |
369 // Adds metadata with a key value that is not "permission". | |
370 TEST_F(SafeBrowsingV4ProtocolManagerTest, | |
371 TestParseHashResponseNonPermissionMetadata) { | |
372 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
373 | |
374 FindFullHashesResponse res; | |
375 res.mutable_negative_cache_duration()->set_seconds(600); | |
376 ThreatMatch* m = res.add_matches(); | |
377 m->set_threat_type(API_ABUSE); | |
378 m->set_platform_type(CHROME_PLATFORM); | |
379 m->set_threat_entry_type(URL_EXPRESSION); | |
380 m->mutable_threat()->set_hash( | |
381 SBFullHashToString(SBFullHashForString("Not to fret."))); | |
382 ThreatEntryMetadata::MetadataEntry* e = | |
383 m->mutable_threat_entry_metadata()->add_entries(); | |
384 e->set_key("notpermission"); | |
385 e->set_value("NOTGEOLOCATION"); | |
386 | |
387 // Serialize. | |
388 std::string res_data; | |
389 res.SerializeToString(&res_data); | |
390 | |
391 std::vector<SBFullHashResult> full_hashes; | |
392 base::TimeDelta cache_lifetime; | |
393 EXPECT_TRUE(pm->ParseHashResponse(res_data, &full_hashes, &cache_lifetime)); | |
394 | |
395 EXPECT_EQ(base::TimeDelta::FromSeconds(600), cache_lifetime); | |
396 EXPECT_EQ(1ul, full_hashes.size()); | |
397 | |
398 EXPECT_TRUE(SBFullHashEqual(SBFullHashForString("Not to fret."), | |
399 full_hashes[0].hash)); | |
400 // Metadata should be empty. | |
401 EXPECT_EQ("", full_hashes[0].metadata); | |
402 EXPECT_EQ(base::TimeDelta::FromSeconds(0), full_hashes[0].cache_duration); | |
403 } | |
404 | |
405 TEST_F(SafeBrowsingV4ProtocolManagerTest, | |
406 TestParseHashResponseInconsistentThreatTypes) { | |
407 scoped_ptr<V4ProtocolManager> pm(CreateProtocolManager()); | |
408 | |
409 FindFullHashesResponse res; | |
410 ThreatMatch* m1 = res.add_matches(); | |
411 m1->set_threat_type(API_ABUSE); | |
412 m1->set_platform_type(CHROME_PLATFORM); | |
413 m1->set_threat_entry_type(URL_EXPRESSION); | |
414 m1->mutable_threat()->set_hash( | |
415 SBFullHashToString(SBFullHashForString("Everything's shiny, Cap'n."))); | |
416 m1->mutable_threat_entry_metadata()->add_entries(); | |
417 ThreatMatch* m2 = res.add_matches(); | |
418 m2->set_threat_type(MALWARE_THREAT); | |
419 m2->set_threat_entry_type(URL_EXPRESSION); | |
420 m2->mutable_threat()->set_hash( | |
421 SBFullHashToString(SBFullHashForString("Not to fret."))); | |
422 | |
423 // Serialize. | |
424 std::string res_data; | |
425 res.SerializeToString(&res_data); | |
426 | |
427 std::vector<SBFullHashResult> full_hashes; | |
428 base::TimeDelta cache_lifetime; | |
429 EXPECT_FALSE(pm->ParseHashResponse(res_data, &full_hashes, &cache_lifetime)); | |
430 } | |
431 | |
432 } // namespace safe_browsing | |
OLD | NEW |