1 // Copyright (c) 2012 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 // Unit tests for the SafeBrowsing storage system. 6 7 #include "base/file_util.h" 8 #include "base/files/scoped_temp_dir.h" 9 #include "base/logging.h" 10 #include "base/memory/scoped_vector.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/sha1.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_split.h" 15 #include "base/time/time.h" 16 #include "chrome/browser/safe_browsing/chunk.pb.h" 17 #include "chrome/browser/safe_browsing/safe_browsing_database.h" 18 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h" 19 #include "content/public/test/test_browser_thread_bundle.h" 20 #include "crypto/sha2.h" 21 #include "net/base/net_util.h" 22 #include "sql/connection.h" 23 #include "sql/statement.h" 24 #include "testing/gtest/include/gtest/gtest.h" 25 #include "testing/platform_test.h" 26 #include "url/gurl.h" 27 28 using base::Time; 29 using base::TimeDelta; 30 31 namespace { 32 33 const TimeDelta kCacheLifetime = TimeDelta::FromMinutes(45); 34 35 SBPrefix SBPrefixForString(const std::string& str) { 36 return SBFullHashForString(str).prefix; 37 } 38 39 std::string HashedIpPrefix(const std::string& ip_prefix, size_t prefix_size) { 40 net::IPAddressNumber ip_number; 41 EXPECT_TRUE(net::ParseIPLiteralToNumber(ip_prefix, &ip_number)); 42 EXPECT_EQ(net::kIPv6AddressSize, ip_number.size()); 43 const std::string hashed_ip_prefix = base::SHA1HashString( 44 net::IPAddressToPackedString(ip_number)); 45 std::string hash(crypto::kSHA256Length, '\0'); 46 hash.replace(0, hashed_ip_prefix.size(), hashed_ip_prefix); 47 hash[base::kSHA1Length] = static_cast<char>(prefix_size); 48 return hash; 49 } 50 51 // Helper to build a chunk. Caller takes ownership. 52 SBChunkData* BuildChunk(int chunk_number, 53 safe_browsing::ChunkData::ChunkType chunk_type, 54 safe_browsing::ChunkData::PrefixType prefix_type, 55 const void* data, size_t data_size, 56 const std::vector<int>& add_chunk_numbers) { 57 scoped_ptr<safe_browsing::ChunkData> raw_data(new safe_browsing::ChunkData); 58 raw_data->set_chunk_number(chunk_number); 59 raw_data->set_chunk_type(chunk_type); 60 raw_data->set_prefix_type(prefix_type); 61 raw_data->set_hashes(data, data_size); 62 raw_data->clear_add_numbers(); 63 for (size_t i = 0; i < add_chunk_numbers.size(); ++i) { 64 raw_data->add_add_numbers(add_chunk_numbers[i]); 65 } 66 67 return new SBChunkData(raw_data.release()); 68 } 69 70 // Create add chunk with a single prefix generated from |value|. 71 SBChunkData* AddChunkPrefixValue(int chunk_number, 72 const std::string& value) { 73 const SBPrefix prefix = SBPrefixForString(value); 74 return BuildChunk(chunk_number, safe_browsing::ChunkData::ADD, 75 safe_browsing::ChunkData::PREFIX_4B, 76 &prefix, sizeof(prefix), 77 std::vector<int>()); 78 } 79 80 // Generate an add chunk with two prefixes. 81 SBChunkData* AddChunkPrefix2Value(int chunk_number, 82 const std::string& value1, 83 const std::string& value2) { 84 const SBPrefix prefixes[2] = { 85 SBPrefixForString(value1), 86 SBPrefixForString(value2), 87 }; 88 return BuildChunk(chunk_number, safe_browsing::ChunkData::ADD, 89 safe_browsing::ChunkData::PREFIX_4B, 90 &prefixes[0], sizeof(prefixes), 91 std::vector<int>()); 92 } 93 94 // Generate an add chunk with four prefixes. 95 SBChunkData* AddChunkPrefix4Value(int chunk_number, 96 const std::string& value1, 97 const std::string& value2, 98 const std::string& value3, 99 const std::string& value4) { 100 const SBPrefix prefixes[4] = { 101 SBPrefixForString(value1), 102 SBPrefixForString(value2), 103 SBPrefixForString(value3), 104 SBPrefixForString(value4), 105 }; 106 return BuildChunk(chunk_number, safe_browsing::ChunkData::ADD, 107 safe_browsing::ChunkData::PREFIX_4B, 108 &prefixes[0], sizeof(prefixes), 109 std::vector<int>()); 110 } 111 112 // Generate an add chunk with a full hash generated from |value|. 113 SBChunkData* AddChunkFullHashValue(int chunk_number, 114 const std::string& value) { 115 const SBFullHash full_hash = SBFullHashForString(value); 116 return BuildChunk(chunk_number, safe_browsing::ChunkData::ADD, 117 safe_browsing::ChunkData::FULL_32B, 118 &full_hash, sizeof(full_hash), 119 std::vector<int>()); 120 } 121 122 // Generate an add chunk with two full hashes. 123 SBChunkData* AddChunkFullHash2Value(int chunk_number, 124 const std::string& value1, 125 const std::string& value2) { 126 const SBFullHash full_hashes[2] = { 127 SBFullHashForString(value1), 128 SBFullHashForString(value2), 129 }; 130 return BuildChunk(chunk_number, safe_browsing::ChunkData::ADD, 131 safe_browsing::ChunkData::FULL_32B, 132 &full_hashes[0], sizeof(full_hashes), 133 std::vector<int>()); 134 } 135 136 // Generate a sub chunk with a prefix generated from |value|. 137 SBChunkData* SubChunkPrefixValue(int chunk_number, 138 const std::string& value, 139 int add_chunk_number) { 140 const SBPrefix prefix = SBPrefixForString(value); 141 return BuildChunk(chunk_number, safe_browsing::ChunkData::SUB, 142 safe_browsing::ChunkData::PREFIX_4B, 143 &prefix, sizeof(prefix), 144 std::vector<int>(1, add_chunk_number)); 145 } 146 147 // Generate a sub chunk with two prefixes. 148 SBChunkData* SubChunkPrefix2Value(int chunk_number, 149 const std::string& value1, 150 int add_chunk_number1, 151 const std::string& value2, 152 int add_chunk_number2) { 153 const SBPrefix prefixes[2] = { 154 SBPrefixForString(value1), 155 SBPrefixForString(value2), 156 }; 157 std::vector<int> add_chunk_numbers; 158 add_chunk_numbers.push_back(add_chunk_number1); 159 add_chunk_numbers.push_back(add_chunk_number2); 160 return BuildChunk(chunk_number, safe_browsing::ChunkData::SUB, 161 safe_browsing::ChunkData::PREFIX_4B, 162 &prefixes[0], sizeof(prefixes), 163 add_chunk_numbers); 164 } 165 166 // Generate a sub chunk with a full hash generated from |value|. 167 SBChunkData* SubChunkFullHashValue(int chunk_number, 168 const std::string& value, 169 int add_chunk_number) { 170 const SBFullHash full_hash = SBFullHashForString(value); 171 return BuildChunk(chunk_number, safe_browsing::ChunkData::SUB, 172 safe_browsing::ChunkData::FULL_32B, 173 &full_hash, sizeof(full_hash), 174 std::vector<int>(1, add_chunk_number)); 175 } 176 177 // Generate an add chunk with a single full hash for the ip blacklist. 178 SBChunkData* AddChunkHashedIpValue(int chunk_number, 179 const std::string& ip_str, 180 size_t prefix_size) { 181 const std::string full_hash_str = HashedIpPrefix(ip_str, prefix_size); 182 EXPECT_EQ(sizeof(SBFullHash), full_hash_str.size()); 183 SBFullHash full_hash; 184 std::memcpy(&(full_hash.full_hash), full_hash_str.data(), sizeof(SBFullHash)); 185 return BuildChunk(chunk_number, safe_browsing::ChunkData::ADD, 186 safe_browsing::ChunkData::FULL_32B, 187 &full_hash, sizeof(full_hash), 188 std::vector<int>()); 189 } 190 191 // Prevent DCHECK from killing tests. 192 // TODO(shess): Pawel disputes the use of this, so the test which uses 193 // it is DISABLED. http://crbug.com/56448 194 class ScopedLogMessageIgnorer { 195 public: 196 ScopedLogMessageIgnorer() { 197 logging::SetLogMessageHandler(&LogMessageIgnorer); 198 } 199 ~ScopedLogMessageIgnorer() { 200 // TODO(shess): Would be better to verify whether anyone else 201 // changed it, and then restore it to the previous value. 202 logging::SetLogMessageHandler(NULL); 203 } 204 205 private: 206 static bool LogMessageIgnorer(int severity, const char* file, int line, 207 size_t message_start, const std::string& str) { 208 // Intercept FATAL, strip the stack backtrace, and log it without 209 // the crash part. 210 if (severity == logging::LOG_FATAL) { 211 size_t newline = str.find('\n'); 212 if (newline != std::string::npos) { 213 const std::string msg = str.substr(0, newline + 1); 214 fprintf(stderr, "%s", msg.c_str()); 215 fflush(stderr); 216 } 217 return true; 218 } 219 220 return false; 221 } 222 }; 223 224 } // namespace 225 226 class SafeBrowsingDatabaseTest : public PlatformTest { 227 public: 228 virtual void SetUp() { 229 PlatformTest::SetUp(); 230 231 // Setup a database in a temporary directory. 232 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 233 database_.reset(new SafeBrowsingDatabaseNew); 234 database_filename_ = 235 temp_dir_.path().AppendASCII("SafeBrowsingTestDatabase"); 236 database_->Init(database_filename_); 237 } 238 239 virtual void TearDown() { 240 database_.reset(); 241 242 PlatformTest::TearDown(); 243 } 244 245 void GetListsInfo(std::vector<SBListChunkRanges>* lists) { 246 lists->clear(); 247 ASSERT_TRUE(database_->UpdateStarted(lists)); 248 database_->UpdateFinished(true); 249 } 250 251 // Helper function to do an AddDel or SubDel command. 252 void DelChunk(const std::string& list, 253 int chunk_id, 254 bool is_sub_del) { 255 std::vector<SBChunkDelete> deletes; 256 SBChunkDelete chunk_delete; 257 chunk_delete.list_name = list; 258 chunk_delete.is_sub_del = is_sub_del; 259 chunk_delete.chunk_del.push_back(ChunkRange(chunk_id)); 260 deletes.push_back(chunk_delete); 261 database_->DeleteChunks(deletes); 262 } 263 264 void AddDelChunk(const std::string& list, int chunk_id) { 265 DelChunk(list, chunk_id, false); 266 } 267 268 void SubDelChunk(const std::string& list, int chunk_id) { 269 DelChunk(list, chunk_id, true); 270 } 271 272 // Utility function for setting up the database for the caching test. 273 void PopulateDatabaseForCacheTest(); 274 275 scoped_ptr<SafeBrowsingDatabaseNew> database_; 276 base::FilePath database_filename_; 277 base::ScopedTempDir temp_dir_; 278 }; 279 280 // Tests retrieving list name information. 281 TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowse) { 282 std::vector<SBListChunkRanges> lists; 283 ScopedVector<SBChunkData> chunks; 284 285 chunks.push_back(AddChunkPrefixValue(1, "www.evil.com/malware.html")); 286 chunks.push_back(AddChunkPrefixValue(2, "www.foo.com/malware.html")); 287 chunks.push_back(AddChunkPrefixValue(3, "www.whatever.com/malware.html")); 288 289 ASSERT_TRUE(database_->UpdateStarted(&lists)); 290 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 291 database_->UpdateFinished(true); 292 293 GetListsInfo(&lists); 294 ASSERT_LE(1U, lists.size()); 295 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 296 EXPECT_EQ("1-3", lists[0].adds); 297 EXPECT_TRUE(lists[0].subs.empty()); 298 299 // Insert a malware sub chunk. 300 chunks.clear(); 301 chunks.push_back(SubChunkPrefixValue(7, "www.subbed.com/noteveil1.html", 19)); 302 303 ASSERT_TRUE(database_->UpdateStarted(&lists)); 304 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 305 database_->UpdateFinished(true); 306 307 GetListsInfo(&lists); 308 ASSERT_LE(1U, lists.size()); 309 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 310 EXPECT_EQ("1-3", lists[0].adds); 311 EXPECT_EQ("7", lists[0].subs); 312 if (lists.size() == 2) { 313 // Old style database won't have the second entry since it creates the lists 314 // when it receives an update containing that list. The filter-based 315 // database has these values hard coded. 316 EXPECT_EQ(safe_browsing_util::kPhishingList, lists[1].name); 317 EXPECT_TRUE(lists[1].adds.empty()); 318 EXPECT_TRUE(lists[1].subs.empty()); 319 } 320 321 // Add phishing chunks. 322 chunks.clear(); 323 chunks.push_back(AddChunkPrefixValue(47, "www.evil.com/phishing.html")); 324 chunks.push_back( 325 SubChunkPrefixValue(200, "www.phishy.com/notevil1.html", 1999)); 326 chunks.push_back( 327 SubChunkPrefixValue(201, "www.phishy2.com/notevil1.html", 1999)); 328 329 ASSERT_TRUE(database_->UpdateStarted(&lists)); 330 database_->InsertChunks(safe_browsing_util::kPhishingList, chunks.get()); 331 database_->UpdateFinished(true); 332 333 GetListsInfo(&lists); 334 ASSERT_EQ(2U, lists.size()); 335 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 336 EXPECT_EQ("1-3", lists[0].adds); 337 EXPECT_EQ("7", lists[0].subs); 338 EXPECT_EQ(safe_browsing_util::kPhishingList, lists[1].name); 339 EXPECT_EQ("47", lists[1].adds); 340 EXPECT_EQ("200-201", lists[1].subs); 341 } 342 343 TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { 344 database_.reset(); 345 base::MessageLoop loop; 346 SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); 347 SafeBrowsingStoreFile* download_store = new SafeBrowsingStoreFile(); 348 SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); 349 SafeBrowsingStoreFile* download_whitelist_store = new SafeBrowsingStoreFile(); 350 SafeBrowsingStoreFile* extension_blacklist_store = 351 new SafeBrowsingStoreFile(); 352 SafeBrowsingStoreFile* ip_blacklist_store = new SafeBrowsingStoreFile(); 353 database_.reset(new SafeBrowsingDatabaseNew(browse_store, 354 download_store, 355 csd_whitelist_store, 356 download_whitelist_store, 357 extension_blacklist_store, 358 NULL, 359 ip_blacklist_store)); 360 database_->Init(database_filename_); 361 362 ScopedVector<SBChunkData> chunks; 363 364 std::vector<SBListChunkRanges> lists; 365 ASSERT_TRUE(database_->UpdateStarted(&lists)); 366 367 // Insert malware, phish, binurl and bindownload add chunks. 368 chunks.push_back(AddChunkPrefixValue(1, "www.evil.com/malware.html")); 369 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 370 371 chunks.clear(); 372 chunks.push_back(AddChunkPrefixValue(2, "www.foo.com/malware.html")); 373 database_->InsertChunks(safe_browsing_util::kPhishingList, chunks.get()); 374 375 chunks.clear(); 376 chunks.push_back(AddChunkPrefixValue(3, "www.whatever.com/download.html")); 377 database_->InsertChunks(safe_browsing_util::kBinUrlList, chunks.get()); 378 379 chunks.clear(); 380 chunks.push_back(AddChunkFullHashValue(5, "www.forwhitelist.com/a.html")); 381 database_->InsertChunks(safe_browsing_util::kCsdWhiteList, chunks.get()); 382 383 chunks.clear(); 384 chunks.push_back(AddChunkFullHashValue(6, "www.download.com/")); 385 database_->InsertChunks(safe_browsing_util::kDownloadWhiteList, chunks.get()); 386 387 chunks.clear(); 388 chunks.push_back(AddChunkFullHashValue(8, 389 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 390 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); 391 database_->InsertChunks(safe_browsing_util::kExtensionBlacklist, 392 chunks.get()); 393 394 chunks.clear(); 395 chunks.push_back(AddChunkHashedIpValue(9, "::ffff:192.168.1.0", 120)); 396 database_->InsertChunks(safe_browsing_util::kIPBlacklist, chunks.get()); 397 398 database_->UpdateFinished(true); 399 400 GetListsInfo(&lists); 401 ASSERT_EQ(7U, lists.size()); 402 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 403 EXPECT_EQ("1", lists[0].adds); 404 EXPECT_TRUE(lists[0].subs.empty()); 405 EXPECT_EQ(safe_browsing_util::kPhishingList, lists[1].name); 406 EXPECT_EQ("2", lists[1].adds); 407 EXPECT_TRUE(lists[1].subs.empty()); 408 EXPECT_EQ(safe_browsing_util::kBinUrlList, lists[2].name); 409 EXPECT_EQ("3", lists[2].adds); 410 EXPECT_TRUE(lists[2].subs.empty()); 411 EXPECT_EQ(safe_browsing_util::kCsdWhiteList, lists[3].name); 412 EXPECT_EQ("5", lists[3].adds); 413 EXPECT_TRUE(lists[3].subs.empty()); 414 EXPECT_EQ(safe_browsing_util::kDownloadWhiteList, lists[4].name); 415 EXPECT_EQ("6", lists[4].adds); 416 EXPECT_TRUE(lists[4].subs.empty()); 417 EXPECT_EQ(safe_browsing_util::kExtensionBlacklist, lists[5].name); 418 EXPECT_EQ("8", lists[5].adds); 419 EXPECT_TRUE(lists[5].subs.empty()); 420 EXPECT_EQ(safe_browsing_util::kIPBlacklist, lists[6].name); 421 EXPECT_EQ("9", lists[6].adds); 422 EXPECT_TRUE(lists[6].subs.empty()); 423 424 database_.reset(); 425 } 426 427 // Checks database reading and writing for browse. 428 TEST_F(SafeBrowsingDatabaseTest, BrowseDatabase) { 429 std::vector<SBListChunkRanges> lists; 430 ScopedVector<SBChunkData> chunks; 431 432 chunks.push_back(AddChunkPrefix2Value(1, 433 "www.evil.com/phishing.html", 434 "www.evil.com/malware.html")); 435 chunks.push_back(AddChunkPrefix4Value(2, 436 "www.evil.com/notevil1.html", 437 "www.evil.com/notevil2.html", 438 "www.good.com/good1.html", 439 "www.good.com/good2.html")); 440 chunks.push_back(AddChunkPrefixValue(3, "192.168.0.1/malware.html")); 441 chunks.push_back(AddChunkFullHashValue(7, "www.evil.com/evil.html")); 442 443 ASSERT_TRUE(database_->UpdateStarted(&lists)); 444 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 445 database_->UpdateFinished(true); 446 447 // Make sure they were added correctly. 448 GetListsInfo(&lists); 449 ASSERT_LE(1U, lists.size()); 450 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 451 EXPECT_EQ("1-3,7", lists[0].adds); 452 EXPECT_TRUE(lists[0].subs.empty()); 453 454 std::vector<SBPrefix> prefix_hits; 455 std::vector<SBFullHashResult> cache_hits; 456 EXPECT_TRUE(database_->ContainsBrowseUrl( 457 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits)); 458 ASSERT_EQ(1U, prefix_hits.size()); 459 EXPECT_EQ(SBPrefixForString("www.evil.com/phishing.html"), prefix_hits[0]); 460 EXPECT_TRUE(cache_hits.empty()); 461 462 EXPECT_TRUE(database_->ContainsBrowseUrl( 463 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits)); 464 465 EXPECT_TRUE(database_->ContainsBrowseUrl( 466 GURL("http://www.evil.com/notevil1.html"), &prefix_hits, &cache_hits)); 467 468 EXPECT_TRUE(database_->ContainsBrowseUrl( 469 GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits)); 470 471 EXPECT_TRUE(database_->ContainsBrowseUrl( 472 GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits)); 473 474 EXPECT_TRUE(database_->ContainsBrowseUrl( 475 GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits)); 476 477 EXPECT_TRUE(database_->ContainsBrowseUrl( 478 GURL("http://192.168.0.1/malware.html"), &prefix_hits, &cache_hits)); 479 480 EXPECT_FALSE(database_->ContainsBrowseUrl( 481 GURL("http://www.evil.com/"), &prefix_hits, &cache_hits)); 482 EXPECT_TRUE(prefix_hits.empty()); 483 EXPECT_TRUE(cache_hits.empty()); 484 485 EXPECT_FALSE(database_->ContainsBrowseUrl( 486 GURL("http://www.evil.com/robots.txt"), &prefix_hits, &cache_hits)); 487 488 EXPECT_TRUE(database_->ContainsBrowseUrl( 489 GURL("http://www.evil.com/evil.html"), &prefix_hits, &cache_hits)); 490 ASSERT_EQ(1U, prefix_hits.size()); 491 EXPECT_EQ(SBPrefixForString("www.evil.com/evil.html"), prefix_hits[0]); 492 493 // Attempt to re-add the first chunk (should be a no-op). 494 // see bug: http://code.google.com/p/chromium/issues/detail?id=4522 495 chunks.clear(); 496 chunks.push_back(AddChunkPrefix2Value(1, 497 "www.evil.com/phishing.html", 498 "www.evil.com/malware.html")); 499 ASSERT_TRUE(database_->UpdateStarted(&lists)); 500 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 501 database_->UpdateFinished(true); 502 503 GetListsInfo(&lists); 504 ASSERT_LE(1U, lists.size()); 505 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 506 EXPECT_EQ("1-3,7", lists[0].adds); 507 EXPECT_TRUE(lists[0].subs.empty()); 508 509 // Test removing a single prefix from the add chunk. 510 chunks.clear(); 511 chunks.push_back(SubChunkPrefixValue(4, "www.evil.com/notevil1.html", 2)); 512 ASSERT_TRUE(database_->UpdateStarted(&lists)); 513 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 514 database_->UpdateFinished(true); 515 516 EXPECT_TRUE(database_->ContainsBrowseUrl( 517 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits)); 518 ASSERT_EQ(1U, prefix_hits.size()); 519 EXPECT_EQ(SBPrefixForString("www.evil.com/phishing.html"), prefix_hits[0]); 520 EXPECT_TRUE(cache_hits.empty()); 521 522 EXPECT_FALSE(database_->ContainsBrowseUrl( 523 GURL("http://www.evil.com/notevil1.html"), &prefix_hits, &cache_hits)); 524 EXPECT_TRUE(prefix_hits.empty()); 525 EXPECT_TRUE(cache_hits.empty()); 526 527 EXPECT_TRUE(database_->ContainsBrowseUrl( 528 GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits)); 529 530 EXPECT_TRUE(database_->ContainsBrowseUrl( 531 GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits)); 532 533 EXPECT_TRUE(database_->ContainsBrowseUrl( 534 GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits)); 535 536 GetListsInfo(&lists); 537 ASSERT_LE(1U, lists.size()); 538 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 539 EXPECT_EQ("1-3,7", lists[0].adds); 540 EXPECT_EQ("4", lists[0].subs); 541 542 // Test the same sub chunk again. This should be a no-op. 543 // see bug: http://code.google.com/p/chromium/issues/detail?id=4522 544 chunks.clear(); 545 chunks.push_back(SubChunkPrefixValue(4, "www.evil.com/notevil1.html", 2)); 546 547 ASSERT_TRUE(database_->UpdateStarted(&lists)); 548 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 549 database_->UpdateFinished(true); 550 551 GetListsInfo(&lists); 552 ASSERT_LE(1U, lists.size()); 553 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 554 EXPECT_EQ("1-3,7", lists[0].adds); 555 EXPECT_EQ("4", lists[0].subs); 556 557 // Test removing all the prefixes from an add chunk. 558 ASSERT_TRUE(database_->UpdateStarted(&lists)); 559 AddDelChunk(safe_browsing_util::kMalwareList, 2); 560 database_->UpdateFinished(true); 561 562 EXPECT_FALSE(database_->ContainsBrowseUrl( 563 GURL("http://www.evil.com/notevil2.html"), &prefix_hits, &cache_hits)); 564 565 EXPECT_FALSE(database_->ContainsBrowseUrl( 566 GURL("http://www.good.com/good1.html"), &prefix_hits, &cache_hits)); 567 568 EXPECT_FALSE(database_->ContainsBrowseUrl( 569 GURL("http://www.good.com/good2.html"), &prefix_hits, &cache_hits)); 570 571 GetListsInfo(&lists); 572 ASSERT_LE(1U, lists.size()); 573 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 574 EXPECT_EQ("1,3,7", lists[0].adds); 575 EXPECT_EQ("4", lists[0].subs); 576 577 // The adddel command exposed a bug in the transaction code where any 578 // transaction after it would fail. Add a dummy entry and remove it to 579 // make sure the transcation works fine. 580 chunks.clear(); 581 chunks.push_back(AddChunkPrefixValue(44, "www.redherring.com/index.html")); 582 ASSERT_TRUE(database_->UpdateStarted(&lists)); 583 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 584 585 // Now remove the dummy entry. If there are any problems with the 586 // transactions, asserts will fire. 587 AddDelChunk(safe_browsing_util::kMalwareList, 44); 588 589 // Test the subdel command. 590 SubDelChunk(safe_browsing_util::kMalwareList, 4); 591 database_->UpdateFinished(true); 592 593 GetListsInfo(&lists); 594 ASSERT_LE(1U, lists.size()); 595 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 596 EXPECT_EQ("1,3,7", lists[0].adds); 597 EXPECT_TRUE(lists[0].subs.empty()); 598 599 // Test a sub command coming in before the add. 600 chunks.clear(); 601 chunks.push_back(SubChunkPrefix2Value(5, 602 "www.notevilanymore.com/index.html", 603 10, 604 "www.notevilanymore.com/good.html", 605 10)); 606 ASSERT_TRUE(database_->UpdateStarted(&lists)); 607 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 608 database_->UpdateFinished(true); 609 610 EXPECT_FALSE(database_->ContainsBrowseUrl( 611 GURL("http://www.notevilanymore.com/index.html"), 612 &prefix_hits, 613 &cache_hits)); 614 615 // Now insert the tardy add chunk and we don't expect them to appear 616 // in database because of the previous sub chunk. 617 chunks.clear(); 618 chunks.push_back(AddChunkPrefix2Value(10, 619 "www.notevilanymore.com/index.html", 620 "www.notevilanymore.com/good.html")); 621 ASSERT_TRUE(database_->UpdateStarted(&lists)); 622 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 623 database_->UpdateFinished(true); 624 625 EXPECT_FALSE(database_->ContainsBrowseUrl( 626 GURL("http://www.notevilanymore.com/index.html"), 627 &prefix_hits, 628 &cache_hits)); 629 630 EXPECT_FALSE(database_->ContainsBrowseUrl( 631 GURL("http://www.notevilanymore.com/good.html"), 632 &prefix_hits, 633 &cache_hits)); 634 635 // Reset and reload the database. The database will rely on the prefix set. 636 database_.reset(new SafeBrowsingDatabaseNew); 637 database_->Init(database_filename_); 638 639 // Check that a prefix still hits. 640 EXPECT_TRUE(database_->ContainsBrowseUrl( 641 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits)); 642 ASSERT_EQ(1U, prefix_hits.size()); 643 EXPECT_EQ(SBPrefixForString("www.evil.com/phishing.html"), prefix_hits[0]); 644 645 // Also check that it's not just always returning true in this case. 646 EXPECT_FALSE(database_->ContainsBrowseUrl( 647 GURL("http://www.evil.com/"), &prefix_hits, &cache_hits)); 648 649 // Check that the full hash is still present. 650 EXPECT_TRUE(database_->ContainsBrowseUrl( 651 GURL("http://www.evil.com/evil.html"), &prefix_hits, &cache_hits)); 652 ASSERT_EQ(1U, prefix_hits.size()); 653 EXPECT_EQ(SBPrefixForString("www.evil.com/evil.html"), prefix_hits[0]); 654 } 655 656 // Test adding zero length chunks to the database. 657 TEST_F(SafeBrowsingDatabaseTest, ZeroSizeChunk) { 658 std::vector<SBListChunkRanges> lists; 659 ScopedVector<SBChunkData> chunks; 660 661 // Populate with a couple of normal chunks. 662 chunks.push_back(AddChunkPrefix2Value(1, 663 "www.test.com/test1.html", 664 "www.test.com/test2.html")); 665 chunks.push_back(AddChunkPrefix2Value(10, 666 "www.random.com/random1.html", 667 "www.random.com/random2.html")); 668 669 ASSERT_TRUE(database_->UpdateStarted(&lists)); 670 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 671 database_->UpdateFinished(true); 672 673 // Add an empty ADD and SUB chunk. 674 GetListsInfo(&lists); 675 ASSERT_LE(1U, lists.size()); 676 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 677 EXPECT_EQ("1,10", lists[0].adds); 678 EXPECT_TRUE(lists[0].subs.empty()); 679 680 chunks.clear(); 681 chunks.push_back(BuildChunk(19, safe_browsing::ChunkData::ADD, 682 safe_browsing::ChunkData::PREFIX_4B, 683 NULL, 0, std::vector<int>())); 684 chunks.push_back(BuildChunk(7, safe_browsing::ChunkData::SUB, 685 safe_browsing::ChunkData::PREFIX_4B, 686 NULL, 0, std::vector<int>())); 687 688 ASSERT_TRUE(database_->UpdateStarted(&lists)); 689 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 690 database_->UpdateFinished(true); 691 692 GetListsInfo(&lists); 693 ASSERT_LE(1U, lists.size()); 694 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 695 EXPECT_EQ("1,10,19", lists[0].adds); 696 EXPECT_EQ("7", lists[0].subs); 697 698 // Add an empty chunk along with a couple that contain data. This should 699 // result in the chunk range being reduced in size. 700 chunks.clear(); 701 chunks.push_back(AddChunkPrefixValue(20, "www.notempty.com/full1.html")); 702 chunks.push_back(BuildChunk(21, safe_browsing::ChunkData::ADD, 703 safe_browsing::ChunkData::PREFIX_4B, 704 NULL, 0, std::vector<int>())); 705 chunks.push_back(AddChunkPrefixValue(22, "www.notempty.com/full2.html")); 706 707 ASSERT_TRUE(database_->UpdateStarted(&lists)); 708 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 709 database_->UpdateFinished(true); 710 711 std::vector<SBPrefix> prefix_hits; 712 std::vector<SBFullHashResult> cache_hits; 713 EXPECT_TRUE(database_->ContainsBrowseUrl( 714 GURL("http://www.notempty.com/full1.html"), &prefix_hits, &cache_hits)); 715 EXPECT_TRUE(database_->ContainsBrowseUrl( 716 GURL("http://www.notempty.com/full2.html"), &prefix_hits, &cache_hits)); 717 718 GetListsInfo(&lists); 719 ASSERT_LE(1U, lists.size()); 720 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 721 EXPECT_EQ("1,10,19-22", lists[0].adds); 722 EXPECT_EQ("7", lists[0].subs); 723 724 // Handle AddDel and SubDel commands for empty chunks. 725 ASSERT_TRUE(database_->UpdateStarted(&lists)); 726 AddDelChunk(safe_browsing_util::kMalwareList, 21); 727 database_->UpdateFinished(true); 728 729 GetListsInfo(&lists); 730 ASSERT_LE(1U, lists.size()); 731 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 732 EXPECT_EQ("1,10,19-20,22", lists[0].adds); 733 EXPECT_EQ("7", lists[0].subs); 734 735 ASSERT_TRUE(database_->UpdateStarted(&lists)); 736 SubDelChunk(safe_browsing_util::kMalwareList, 7); 737 database_->UpdateFinished(true); 738 739 GetListsInfo(&lists); 740 ASSERT_LE(1U, lists.size()); 741 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 742 EXPECT_EQ("1,10,19-20,22", lists[0].adds); 743 EXPECT_TRUE(lists[0].subs.empty()); 744 } 745 746 // Utility function for setting up the database for the caching test. 747 void SafeBrowsingDatabaseTest::PopulateDatabaseForCacheTest() { 748 // Add a simple chunk with one hostkey and cache it. 749 ScopedVector<SBChunkData> chunks; 750 chunks.push_back(AddChunkPrefix2Value(1, 751 "www.evil.com/phishing.html", 752 "www.evil.com/malware.html")); 753 754 std::vector<SBListChunkRanges> lists; 755 ASSERT_TRUE(database_->UpdateStarted(&lists)); 756 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 757 database_->UpdateFinished(true); 758 759 // Add the GetHash results to the cache. 760 SBFullHashResult full_hash; 761 full_hash.hash = SBFullHashForString("www.evil.com/phishing.html"); 762 full_hash.list_id = safe_browsing_util::MALWARE; 763 764 std::vector<SBFullHashResult> results; 765 results.push_back(full_hash); 766 767 full_hash.hash = SBFullHashForString("www.evil.com/malware.html"); 768 results.push_back(full_hash); 769 770 std::vector<SBPrefix> prefixes; 771 database_->CacheHashResults(prefixes, results, kCacheLifetime); 772 } 773 774 TEST_F(SafeBrowsingDatabaseTest, HashCaching) { 775 PopulateDatabaseForCacheTest(); 776 777 // We should have both full hashes in the cache. 778 EXPECT_EQ(2U, database_->cached_browse_hashes_.size()); 779 780 // Test the cache lookup for the first prefix. 781 std::vector<SBPrefix> prefix_hits; 782 std::vector<SBFullHashResult> cache_hits; 783 database_->ContainsBrowseUrl( 784 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits); 785 ASSERT_EQ(1U, cache_hits.size()); 786 EXPECT_TRUE(SBFullHashEqual( 787 cache_hits[0].hash, SBFullHashForString("www.evil.com/phishing.html"))); 788 789 prefix_hits.clear(); 790 cache_hits.clear(); 791 792 // Test the cache lookup for the second prefix. 793 database_->ContainsBrowseUrl( 794 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits); 795 ASSERT_EQ(1U, cache_hits.size()); 796 EXPECT_TRUE(SBFullHashEqual( 797 cache_hits[0].hash, SBFullHashForString("www.evil.com/malware.html"))); 798 799 prefix_hits.clear(); 800 cache_hits.clear(); 801 802 // Test removing a prefix via a sub chunk. 803 ScopedVector<SBChunkData> chunks; 804 chunks.push_back(SubChunkPrefixValue(2, "www.evil.com/phishing.html", 1)); 805 806 std::vector<SBListChunkRanges> lists; 807 ASSERT_TRUE(database_->UpdateStarted(&lists)); 808 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 809 database_->UpdateFinished(true); 810 811 // This prefix should still be there, but cached fullhash should be gone. 812 EXPECT_TRUE(database_->ContainsBrowseUrl( 813 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits)); 814 ASSERT_EQ(1U, prefix_hits.size()); 815 EXPECT_EQ(SBPrefixForString("www.evil.com/malware.html"), prefix_hits[0]); 816 EXPECT_TRUE(cache_hits.empty()); 817 prefix_hits.clear(); 818 cache_hits.clear(); 819 820 // This prefix should be gone. 821 database_->ContainsBrowseUrl( 822 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits); 823 EXPECT_TRUE(prefix_hits.empty()); 824 EXPECT_TRUE(cache_hits.empty()); 825 826 prefix_hits.clear(); 827 cache_hits.clear(); 828 829 // Test that an AddDel for the original chunk removes the last cached entry. 830 ASSERT_TRUE(database_->UpdateStarted(&lists)); 831 AddDelChunk(safe_browsing_util::kMalwareList, 1); 832 database_->UpdateFinished(true); 833 database_->ContainsBrowseUrl( 834 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits); 835 EXPECT_TRUE(cache_hits.empty()); 836 EXPECT_TRUE(database_->cached_browse_hashes_.empty()); 837 838 prefix_hits.clear(); 839 cache_hits.clear(); 840 841 // Test that the cache won't return expired values. First we have to adjust 842 // the cached entries' received time to make them older, since the database 843 // cache insert uses Time::Now(). First, store some entries. 844 PopulateDatabaseForCacheTest(); 845 846 std::vector<SBFullHashCached>* hash_cache = &database_->cached_browse_hashes_; 847 EXPECT_EQ(2U, hash_cache->size()); 848 849 // Now adjust one of the entries times to be expired. 850 const Time expired = Time::Now() - TimeDelta::FromMinutes(1); 851 const SBPrefix key = SBPrefixForString("www.evil.com/malware.html"); 852 std::vector<SBFullHashCached>::iterator iter; 853 for (iter = hash_cache->begin(); iter != hash_cache->end(); ++iter) { 854 if (iter->hash.prefix == key) { 855 iter->expire_after = expired; 856 break; 857 } 858 } 859 EXPECT_TRUE(iter != hash_cache->end()); 860 861 database_->ContainsBrowseUrl( 862 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits); 863 EXPECT_TRUE(cache_hits.empty()); 864 865 // This entry should still exist. 866 database_->ContainsBrowseUrl( 867 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits); 868 EXPECT_EQ(1U, cache_hits.size()); 869 870 // Testing prefix miss caching. First, we clear out the existing database, 871 // Since PopulateDatabaseForCacheTest() doesn't handle adding duplicate 872 // chunks. 873 ASSERT_TRUE(database_->UpdateStarted(&lists)); 874 AddDelChunk(safe_browsing_util::kMalwareList, 1); 875 database_->UpdateFinished(true); 876 877 std::vector<SBPrefix> prefix_misses; 878 std::vector<SBFullHashResult> empty_full_hash; 879 prefix_misses.push_back(SBPrefixForString("http://www.bad.com/malware.html")); 880 prefix_misses.push_back( 881 SBPrefixForString("http://www.bad.com/phishing.html")); 882 database_->CacheHashResults(prefix_misses, empty_full_hash, kCacheLifetime); 883 884 // Prefixes with no full results are misses. 885 EXPECT_EQ(2U, database_->prefix_miss_cache_.size()); 886 887 // Update the database. 888 PopulateDatabaseForCacheTest(); 889 890 // Prefix miss cache should be cleared. 891 EXPECT_TRUE(database_->prefix_miss_cache_.empty()); 892 893 // Cache a GetHash miss for a particular prefix, and even though the prefix is 894 // in the database, it is flagged as a miss so looking up the associated URL 895 // will not succeed. 896 prefix_hits.clear(); 897 cache_hits.clear(); 898 prefix_misses.clear(); 899 empty_full_hash.clear(); 900 prefix_misses.push_back(SBPrefixForString("www.evil.com/phishing.html")); 901 database_->CacheHashResults(prefix_misses, empty_full_hash, kCacheLifetime); 902 EXPECT_FALSE(database_->ContainsBrowseUrl( 903 GURL("http://www.evil.com/phishing.html"), &prefix_hits, &cache_hits)); 904 905 prefix_hits.clear(); 906 cache_hits.clear(); 907 908 // Test receiving a full add chunk. 909 chunks.clear(); 910 chunks.push_back(AddChunkFullHash2Value(20, 911 "www.fullevil.com/bad1.html", 912 "www.fullevil.com/bad2.html")); 913 ASSERT_TRUE(database_->UpdateStarted(&lists)); 914 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 915 database_->UpdateFinished(true); 916 917 EXPECT_TRUE(database_->ContainsBrowseUrl( 918 GURL("http://www.fullevil.com/bad1.html"), &prefix_hits, &cache_hits)); 919 ASSERT_EQ(1U, prefix_hits.size()); 920 EXPECT_EQ(SBPrefixForString("www.fullevil.com/bad1.html"), prefix_hits[0]); 921 EXPECT_TRUE(cache_hits.empty()); 922 prefix_hits.clear(); 923 cache_hits.clear(); 924 925 EXPECT_TRUE(database_->ContainsBrowseUrl( 926 GURL("http://www.fullevil.com/bad2.html"), &prefix_hits, &cache_hits)); 927 ASSERT_EQ(1U, prefix_hits.size()); 928 EXPECT_EQ(SBPrefixForString("www.fullevil.com/bad2.html"), prefix_hits[0]); 929 EXPECT_TRUE(cache_hits.empty()); 930 prefix_hits.clear(); 931 cache_hits.clear(); 932 933 // Test receiving a full sub chunk, which will remove one of the full adds. 934 chunks.clear(); 935 chunks.push_back(SubChunkFullHashValue(200, 936 "www.fullevil.com/bad1.html", 937 20)); 938 ASSERT_TRUE(database_->UpdateStarted(&lists)); 939 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 940 database_->UpdateFinished(true); 941 942 EXPECT_FALSE(database_->ContainsBrowseUrl( 943 GURL("http://www.fullevil.com/bad1.html"), &prefix_hits, &cache_hits)); 944 EXPECT_TRUE(prefix_hits.empty()); 945 EXPECT_TRUE(cache_hits.empty()); 946 947 // There should be one remaining full add. 948 EXPECT_TRUE(database_->ContainsBrowseUrl( 949 GURL("http://www.fullevil.com/bad2.html"), &prefix_hits, &cache_hits)); 950 ASSERT_EQ(1U, prefix_hits.size()); 951 EXPECT_EQ(SBPrefixForString("www.fullevil.com/bad2.html"), prefix_hits[0]); 952 EXPECT_TRUE(cache_hits.empty()); 953 prefix_hits.clear(); 954 cache_hits.clear(); 955 956 // Now test an AddDel for the remaining full add. 957 ASSERT_TRUE(database_->UpdateStarted(&lists)); 958 AddDelChunk(safe_browsing_util::kMalwareList, 20); 959 database_->UpdateFinished(true); 960 961 EXPECT_FALSE(database_->ContainsBrowseUrl( 962 GURL("http://www.fullevil.com/bad1.html"), &prefix_hits, &cache_hits)); 963 EXPECT_FALSE(database_->ContainsBrowseUrl( 964 GURL("http://www.fullevil.com/bad2.html"), &prefix_hits, &cache_hits)); 965 966 // Add a fullhash which has a prefix collision for a known url. 967 static const char kExampleFine[] = "www.example.com/fine.html"; 968 static const char kExampleCollision[] = 969 "www.example.com/3123364814/malware.htm"; 970 ASSERT_EQ(SBPrefixForString(kExampleFine), 971 SBPrefixForString(kExampleCollision)); 972 ASSERT_TRUE(database_->UpdateStarted(&lists)); 973 { 974 ScopedVector<SBChunkData> chunks; 975 chunks.push_back(AddChunkPrefixValue(21, kExampleCollision)); 976 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 977 } 978 database_->UpdateFinished(true); 979 980 // Cache gethash response for |kExampleCollision|. 981 { 982 SBFullHashResult result; 983 result.hash = SBFullHashForString(kExampleCollision); 984 result.list_id = safe_browsing_util::MALWARE; 985 database_->CacheHashResults(std::vector<SBPrefix>(1, result.hash.prefix), 986 std::vector<SBFullHashResult>(1, result), 987 kCacheLifetime); 988 } 989 990 // Expect a prefix hit due to the collision between |kExampleFine| and 991 // |kExampleCollision|, with the gethash showing only |kExampleCollision|. 992 EXPECT_TRUE(database_->ContainsBrowseUrl( 993 GURL(std::string("http://") + kExampleFine), &prefix_hits, &cache_hits)); 994 ASSERT_EQ(1U, prefix_hits.size()); 995 EXPECT_EQ(SBPrefixForString(kExampleFine), prefix_hits[0]); 996 ASSERT_EQ(1U, cache_hits.size()); 997 EXPECT_TRUE(SBFullHashEqual(cache_hits[0].hash, 998 SBFullHashForString(kExampleCollision))); 999 } 1000 1001 // Test that corrupt databases are appropriately handled, even if the 1002 // corruption is detected in the midst of the update. 1003 // TODO(shess): Disabled until ScopedLogMessageIgnorer resolved. 1004 // http://crbug.com/56448 1005 TEST_F(SafeBrowsingDatabaseTest, DISABLED_FileCorruptionHandling) { 1006 // Re-create the database in a captive message loop so that we can 1007 // influence task-posting. Database specifically needs to the 1008 // file-backed. 1009 database_.reset(); 1010 base::MessageLoop loop; 1011 SafeBrowsingStoreFile* store = new SafeBrowsingStoreFile(); 1012 database_.reset(new SafeBrowsingDatabaseNew(store, NULL, NULL, NULL, NULL, 1013 NULL, NULL)); 1014 database_->Init(database_filename_); 1015 1016 // This will cause an empty database to be created. 1017 std::vector<SBListChunkRanges> lists; 1018 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1019 database_->UpdateFinished(true); 1020 1021 // Create a sub chunk to insert. 1022 ScopedVector<SBChunkData> chunks; 1023 chunks.push_back(SubChunkPrefixValue(7, 1024 "www.subbed.com/notevil1.html", 1025 19)); 1026 1027 // Corrupt the file by corrupting the checksum, which is not checked 1028 // until the entire table is read in |UpdateFinished()|. 1029 FILE* fp = base::OpenFile(database_filename_, "r+"); 1030 ASSERT_TRUE(fp); 1031 ASSERT_NE(-1, fseek(fp, -8, SEEK_END)); 1032 for (size_t i = 0; i < 8; ++i) { 1033 fputc('!', fp); 1034 } 1035 fclose(fp); 1036 1037 { 1038 // The following code will cause DCHECKs, so suppress the crashes. 1039 ScopedLogMessageIgnorer ignorer; 1040 1041 // Start an update. The insert will fail due to corruption. 1042 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1043 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1044 database_->UpdateFinished(true); 1045 1046 // Database file still exists until the corruption handler has run. 1047 EXPECT_TRUE(base::PathExists(database_filename_)); 1048 1049 // Flush through the corruption-handler task. 1050 VLOG(1) << "Expect failed check on: SafeBrowsing database reset"; 1051 base::MessageLoop::current()->RunUntilIdle(); 1052 } 1053 1054 // Database file should not exist. 1055 EXPECT_FALSE(base::PathExists(database_filename_)); 1056 1057 // Run the update again successfully. 1058 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1059 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1060 database_->UpdateFinished(true); 1061 EXPECT_TRUE(base::PathExists(database_filename_)); 1062 1063 database_.reset(); 1064 } 1065 1066 // Checks database reading and writing. 1067 TEST_F(SafeBrowsingDatabaseTest, ContainsDownloadUrl) { 1068 database_.reset(); 1069 base::MessageLoop loop; 1070 SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); 1071 SafeBrowsingStoreFile* download_store = new SafeBrowsingStoreFile(); 1072 SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); 1073 database_.reset(new SafeBrowsingDatabaseNew(browse_store, 1074 download_store, 1075 csd_whitelist_store, 1076 NULL, 1077 NULL, 1078 NULL, 1079 NULL)); 1080 database_->Init(database_filename_); 1081 1082 const char kEvil1Url1[] = "www.evil1.com/download1/"; 1083 const char kEvil1Url2[] = "www.evil1.com/download2.html"; 1084 1085 // Add a simple chunk with one hostkey for download url list. 1086 ScopedVector<SBChunkData> chunks; 1087 chunks.push_back(AddChunkPrefix2Value(1, kEvil1Url1, kEvil1Url2)); 1088 1089 std::vector<SBListChunkRanges> lists; 1090 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1091 database_->InsertChunks(safe_browsing_util::kBinUrlList, chunks.get()); 1092 database_->UpdateFinished(true); 1093 1094 std::vector<SBPrefix> prefix_hits; 1095 std::vector<GURL> urls(1); 1096 1097 urls[0] = GURL(std::string("http://") + kEvil1Url1); 1098 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1099 ASSERT_EQ(1U, prefix_hits.size()); 1100 EXPECT_EQ(SBPrefixForString(kEvil1Url1), prefix_hits[0]); 1101 1102 urls[0] = GURL(std::string("http://") + kEvil1Url2); 1103 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1104 ASSERT_EQ(1U, prefix_hits.size()); 1105 EXPECT_EQ(SBPrefixForString(kEvil1Url2), prefix_hits[0]); 1106 1107 urls[0] = GURL(std::string("https://") + kEvil1Url2); 1108 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1109 ASSERT_EQ(1U, prefix_hits.size()); 1110 EXPECT_EQ(SBPrefixForString(kEvil1Url2), prefix_hits[0]); 1111 1112 urls[0] = GURL(std::string("ftp://") + kEvil1Url2); 1113 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1114 ASSERT_EQ(1U, prefix_hits.size()); 1115 EXPECT_EQ(SBPrefixForString(kEvil1Url2), prefix_hits[0]); 1116 1117 urls[0] = GURL("http://www.randomevil.com"); 1118 EXPECT_FALSE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1119 1120 // Should match with query args stripped. 1121 urls[0] = GURL(std::string("http://") + kEvil1Url2 + "?blah"); 1122 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1123 ASSERT_EQ(1U, prefix_hits.size()); 1124 EXPECT_EQ(SBPrefixForString(kEvil1Url2), prefix_hits[0]); 1125 1126 // Should match with extra path stuff and query args stripped. 1127 urls[0] = GURL(std::string("http://") + kEvil1Url1 + "foo/bar?blah"); 1128 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1129 ASSERT_EQ(1U, prefix_hits.size()); 1130 EXPECT_EQ(SBPrefixForString(kEvil1Url1), prefix_hits[0]); 1131 1132 // First hit in redirect chain is malware. 1133 urls.clear(); 1134 urls.push_back(GURL(std::string("http://") + kEvil1Url1)); 1135 urls.push_back(GURL("http://www.randomevil.com")); 1136 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1137 ASSERT_EQ(1U, prefix_hits.size()); 1138 EXPECT_EQ(SBPrefixForString(kEvil1Url1), prefix_hits[0]); 1139 1140 // Middle hit in redirect chain is malware. 1141 urls.clear(); 1142 urls.push_back(GURL("http://www.randomevil.com")); 1143 urls.push_back(GURL(std::string("http://") + kEvil1Url1)); 1144 urls.push_back(GURL("http://www.randomevil2.com")); 1145 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1146 ASSERT_EQ(1U, prefix_hits.size()); 1147 EXPECT_EQ(SBPrefixForString(kEvil1Url1), prefix_hits[0]); 1148 1149 // Final hit in redirect chain is malware. 1150 urls.clear(); 1151 urls.push_back(GURL("http://www.randomevil.com")); 1152 urls.push_back(GURL(std::string("http://") + kEvil1Url1)); 1153 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1154 ASSERT_EQ(1U, prefix_hits.size()); 1155 EXPECT_EQ(SBPrefixForString(kEvil1Url1), prefix_hits[0]); 1156 1157 // Multiple hits in redirect chain are in malware list. 1158 urls.clear(); 1159 urls.push_back(GURL(std::string("http://") + kEvil1Url1)); 1160 urls.push_back(GURL(std::string("https://") + kEvil1Url2)); 1161 EXPECT_TRUE(database_->ContainsDownloadUrl(urls, &prefix_hits)); 1162 ASSERT_EQ(2U, prefix_hits.size()); 1163 EXPECT_EQ(SBPrefixForString(kEvil1Url1), prefix_hits[0]); 1164 EXPECT_EQ(SBPrefixForString(kEvil1Url2), prefix_hits[1]); 1165 database_.reset(); 1166 } 1167 1168 // Checks that the whitelists are handled properly. 1169 TEST_F(SafeBrowsingDatabaseTest, Whitelists) { 1170 database_.reset(); 1171 1172 // We expect all calls to ContainsCsdWhitelistedUrl in particular to be made 1173 // from the IO thread. In general the whitelist lookups are thread-safe. 1174 content::TestBrowserThreadBundle thread_bundle_; 1175 1176 // If the whitelist is disabled everything should match the whitelist. 1177 database_.reset(new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile(), 1178 NULL, NULL, NULL, NULL, NULL, 1179 NULL)); 1180 database_->Init(database_filename_); 1181 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1182 GURL(std::string("http://www.phishing.com/")))); 1183 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1184 GURL(std::string("http://www.phishing.com/")))); 1185 EXPECT_TRUE(database_->ContainsDownloadWhitelistedString("asdf")); 1186 1187 SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); 1188 SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); 1189 SafeBrowsingStoreFile* download_whitelist_store = new SafeBrowsingStoreFile(); 1190 SafeBrowsingStoreFile* extension_blacklist_store = 1191 new SafeBrowsingStoreFile(); 1192 database_.reset(new SafeBrowsingDatabaseNew(browse_store, NULL, 1193 csd_whitelist_store, 1194 download_whitelist_store, 1195 extension_blacklist_store, 1196 NULL, NULL)); 1197 database_->Init(database_filename_); 1198 1199 const char kGood1Host[] = "www.good1.com/"; 1200 const char kGood1Url1[] = "www.good1.com/a/b.html"; 1201 const char kGood1Url2[] = "www.good1.com/b/"; 1202 1203 const char kGood2Url1[] = "www.good2.com/c"; // Should match '/c/bla'. 1204 1205 // good3.com/a/b/c/d/e/f/g/ should match because it's a whitelist. 1206 const char kGood3Url1[] = "good3.com/"; 1207 1208 const char kGoodString[] = "good_string"; 1209 1210 ScopedVector<SBChunkData> csd_chunks; 1211 ScopedVector<SBChunkData> download_chunks; 1212 1213 // Add two simple chunks to the csd whitelist. 1214 csd_chunks.push_back(AddChunkFullHash2Value(1, kGood1Url1, kGood1Url2)); 1215 csd_chunks.push_back(AddChunkFullHashValue(2, kGood2Url1)); 1216 download_chunks.push_back(AddChunkFullHashValue(2, kGood2Url1)); 1217 download_chunks.push_back(AddChunkFullHashValue(3, kGoodString)); 1218 download_chunks.push_back(AddChunkFullHashValue(4, kGood3Url1)); 1219 1220 std::vector<SBListChunkRanges> lists; 1221 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1222 database_->InsertChunks(safe_browsing_util::kCsdWhiteList, csd_chunks.get()); 1223 database_->InsertChunks(safe_browsing_util::kDownloadWhiteList, 1224 download_chunks.get()); 1225 database_->UpdateFinished(true); 1226 1227 EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( 1228 GURL(std::string("http://") + kGood1Host))); 1229 1230 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1231 GURL(std::string("http://") + kGood1Url1))); 1232 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1233 GURL(std::string("http://") + kGood1Url1 + "?a=b"))); 1234 1235 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1236 GURL(std::string("http://") + kGood1Url2))); 1237 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1238 GURL(std::string("http://") + kGood1Url2 + "/c.html"))); 1239 1240 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1241 GURL(std::string("https://") + kGood1Url2 + "/c.html"))); 1242 1243 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1244 GURL(std::string("http://") + kGood2Url1 + "/c"))); 1245 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1246 GURL(std::string("http://") + kGood2Url1 + "/c?bla"))); 1247 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1248 GURL(std::string("http://") + kGood2Url1 + "/c/bla"))); 1249 1250 EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( 1251 GURL(std::string("http://www.google.com/")))); 1252 1253 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1254 GURL(std::string("http://") + kGood2Url1 + "/c"))); 1255 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1256 GURL(std::string("http://") + kGood2Url1 + "/c?bla"))); 1257 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1258 GURL(std::string("http://") + kGood2Url1 + "/c/bla"))); 1259 1260 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1261 GURL(std::string("http://good3.com/a/b/c/d/e/f/g/")))); 1262 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1263 GURL(std::string("http://a.b.good3.com/")))); 1264 1265 EXPECT_FALSE(database_->ContainsDownloadWhitelistedString("asdf")); 1266 EXPECT_TRUE(database_->ContainsDownloadWhitelistedString(kGoodString)); 1267 1268 EXPECT_FALSE(database_->ContainsDownloadWhitelistedUrl( 1269 GURL(std::string("http://www.google.com/")))); 1270 1271 // The CSD whitelist killswitch is not present. 1272 EXPECT_FALSE(database_->IsCsdWhitelistKillSwitchOn()); 1273 1274 // Test only add the malware IP killswitch 1275 csd_chunks.clear(); 1276 csd_chunks.push_back(AddChunkFullHashValue( 1277 15, "sb-ssl.google.com/safebrowsing/csd/killswitch_malware")); 1278 1279 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1280 database_->InsertChunks(safe_browsing_util::kCsdWhiteList, csd_chunks.get()); 1281 database_->UpdateFinished(true); 1282 1283 EXPECT_TRUE(database_->IsMalwareIPMatchKillSwitchOn()); 1284 // The CSD whitelist killswitch is not present. 1285 EXPECT_FALSE(database_->IsCsdWhitelistKillSwitchOn()); 1286 1287 // Test that the kill-switch works as intended. 1288 csd_chunks.clear(); 1289 download_chunks.clear(); 1290 lists.clear(); 1291 csd_chunks.push_back(AddChunkFullHashValue( 1292 5, "sb-ssl.google.com/safebrowsing/csd/killswitch")); 1293 download_chunks.push_back(AddChunkFullHashValue( 1294 5, "sb-ssl.google.com/safebrowsing/csd/killswitch")); 1295 1296 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1297 database_->InsertChunks(safe_browsing_util::kCsdWhiteList, csd_chunks.get()); 1298 database_->InsertChunks(safe_browsing_util::kDownloadWhiteList, 1299 download_chunks.get()); 1300 database_->UpdateFinished(true); 1301 1302 // The CSD whitelist killswitch is present. 1303 EXPECT_TRUE(database_->IsCsdWhitelistKillSwitchOn()); 1304 EXPECT_TRUE(database_->IsMalwareIPMatchKillSwitchOn()); 1305 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1306 GURL(std::string("https://") + kGood1Url2 + "/c.html"))); 1307 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1308 GURL(std::string("http://www.google.com/")))); 1309 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1310 GURL(std::string("http://www.phishing_url.com/")))); 1311 1312 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1313 GURL(std::string("https://") + kGood1Url2 + "/c.html"))); 1314 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1315 GURL(std::string("http://www.google.com/")))); 1316 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1317 GURL(std::string("http://www.phishing_url.com/")))); 1318 1319 EXPECT_TRUE(database_->ContainsDownloadWhitelistedString("asdf")); 1320 EXPECT_TRUE(database_->ContainsDownloadWhitelistedString(kGoodString)); 1321 1322 // Remove the kill-switch and verify that we can recover. 1323 csd_chunks.clear(); 1324 download_chunks.clear(); 1325 lists.clear(); 1326 csd_chunks.push_back(SubChunkFullHashValue( 1327 1, "sb-ssl.google.com/safebrowsing/csd/killswitch", 5)); 1328 csd_chunks.push_back(SubChunkFullHashValue( 1329 10, "sb-ssl.google.com/safebrowsing/csd/killswitch_malware", 15)); 1330 download_chunks.push_back(SubChunkFullHashValue( 1331 1, "sb-ssl.google.com/safebrowsing/csd/killswitch", 5)); 1332 1333 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1334 database_->InsertChunks(safe_browsing_util::kCsdWhiteList, csd_chunks.get()); 1335 database_->InsertChunks(safe_browsing_util::kDownloadWhiteList, 1336 download_chunks.get()); 1337 database_->UpdateFinished(true); 1338 1339 EXPECT_FALSE(database_->IsMalwareIPMatchKillSwitchOn()); 1340 EXPECT_FALSE(database_->IsCsdWhitelistKillSwitchOn()); 1341 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1342 GURL(std::string("https://") + kGood1Url2 + "/c.html"))); 1343 EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( 1344 GURL(std::string("https://") + kGood2Url1 + "/c/bla"))); 1345 EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( 1346 GURL(std::string("http://www.google.com/")))); 1347 EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( 1348 GURL(std::string("http://www.phishing_url.com/")))); 1349 1350 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1351 GURL(std::string("https://") + kGood2Url1 + "/c/bla"))); 1352 EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( 1353 GURL(std::string("https://good3.com/")))); 1354 EXPECT_TRUE(database_->ContainsDownloadWhitelistedString(kGoodString)); 1355 EXPECT_FALSE(database_->ContainsDownloadWhitelistedUrl( 1356 GURL(std::string("http://www.google.com/")))); 1357 EXPECT_FALSE(database_->ContainsDownloadWhitelistedUrl( 1358 GURL(std::string("http://www.phishing_url.com/")))); 1359 EXPECT_FALSE(database_->ContainsDownloadWhitelistedString("asdf")); 1360 1361 database_.reset(); 1362 } 1363 1364 // Test to make sure we could insert chunk list that 1365 // contains entries for the same host. 1366 TEST_F(SafeBrowsingDatabaseTest, SameHostEntriesOkay) { 1367 ScopedVector<SBChunkData> chunks; 1368 1369 // Add a malware add chunk with two entries of the same host. 1370 chunks.push_back(AddChunkPrefix2Value(1, 1371 "www.evil.com/malware1.html", 1372 "www.evil.com/malware2.html")); 1373 1374 // Insert the testing chunks into database. 1375 std::vector<SBListChunkRanges> lists; 1376 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1377 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1378 database_->UpdateFinished(true); 1379 1380 GetListsInfo(&lists); 1381 ASSERT_LE(1U, lists.size()); 1382 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 1383 EXPECT_EQ("1", lists[0].adds); 1384 EXPECT_TRUE(lists[0].subs.empty()); 1385 1386 // Add a phishing add chunk with two entries of the same host. 1387 chunks.clear(); 1388 chunks.push_back(AddChunkPrefix2Value(47, 1389 "www.evil.com/phishing1.html", 1390 "www.evil.com/phishing2.html")); 1391 1392 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1393 database_->InsertChunks(safe_browsing_util::kPhishingList, chunks.get()); 1394 database_->UpdateFinished(true); 1395 1396 GetListsInfo(&lists); 1397 ASSERT_EQ(2U, lists.size()); 1398 EXPECT_EQ(safe_browsing_util::kMalwareList, lists[0].name); 1399 EXPECT_EQ("1", lists[0].adds); 1400 EXPECT_TRUE(lists[0].subs.empty()); 1401 EXPECT_EQ(safe_browsing_util::kPhishingList, lists[1].name); 1402 EXPECT_EQ("47", lists[1].adds); 1403 EXPECT_TRUE(lists[1].subs.empty()); 1404 1405 std::vector<SBPrefix> prefix_hits; 1406 std::vector<SBFullHashResult> cache_hits; 1407 1408 EXPECT_TRUE(database_->ContainsBrowseUrl( 1409 GURL("http://www.evil.com/malware1.html"), &prefix_hits, &cache_hits)); 1410 EXPECT_TRUE(database_->ContainsBrowseUrl( 1411 GURL("http://www.evil.com/malware2.html"), &prefix_hits, &cache_hits)); 1412 EXPECT_TRUE(database_->ContainsBrowseUrl( 1413 GURL("http://www.evil.com/phishing1.html"), &prefix_hits, &cache_hits)); 1414 EXPECT_TRUE(database_->ContainsBrowseUrl( 1415 GURL("http://www.evil.com/phishing2.html"), &prefix_hits, &cache_hits)); 1416 1417 // Test removing a single prefix from the add chunk. 1418 // Remove the prefix that added first. 1419 chunks.clear(); 1420 chunks.push_back(SubChunkPrefixValue(4, "www.evil.com/malware1.html", 1)); 1421 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1422 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1423 database_->UpdateFinished(true); 1424 1425 // Remove the prefix that added last. 1426 chunks.clear(); 1427 chunks.push_back(SubChunkPrefixValue(5, "www.evil.com/phishing2.html", 47)); 1428 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1429 database_->InsertChunks(safe_browsing_util::kPhishingList, chunks.get()); 1430 database_->UpdateFinished(true); 1431 1432 // Verify that the database contains urls expected. 1433 EXPECT_FALSE(database_->ContainsBrowseUrl( 1434 GURL("http://www.evil.com/malware1.html"), &prefix_hits, &cache_hits)); 1435 EXPECT_TRUE(database_->ContainsBrowseUrl( 1436 GURL("http://www.evil.com/malware2.html"), &prefix_hits, &cache_hits)); 1437 EXPECT_TRUE(database_->ContainsBrowseUrl( 1438 GURL("http://www.evil.com/phishing1.html"), &prefix_hits, &cache_hits)); 1439 EXPECT_FALSE(database_->ContainsBrowseUrl( 1440 GURL("http://www.evil.com/phishing2.html"), &prefix_hits, &cache_hits)); 1441 } 1442 1443 // Test that an empty update doesn't actually update the database. 1444 // This isn't a functionality requirement, but it is a useful 1445 // optimization. 1446 TEST_F(SafeBrowsingDatabaseTest, EmptyUpdate) { 1447 ScopedVector<SBChunkData> chunks; 1448 1449 base::FilePath filename = database_->BrowseDBFilename(database_filename_); 1450 1451 // Prime the database. 1452 std::vector<SBListChunkRanges> lists; 1453 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1454 chunks.push_back(AddChunkPrefixValue(1, "www.evil.com/malware.html")); 1455 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1456 database_->UpdateFinished(true); 1457 1458 // Get an older time to reset the lastmod time for detecting whether 1459 // the file has been updated. 1460 base::File::Info before_info, after_info; 1461 ASSERT_TRUE(base::GetFileInfo(filename, &before_info)); 1462 const Time old_last_modified = 1463 before_info.last_modified - TimeDelta::FromSeconds(10); 1464 1465 // Inserting another chunk updates the database file. The sleep is 1466 // needed because otherwise the entire test can finish w/in the 1467 // resolution of the lastmod time. 1468 ASSERT_TRUE(base::TouchFile(filename, old_last_modified, old_last_modified)); 1469 ASSERT_TRUE(base::GetFileInfo(filename, &before_info)); 1470 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1471 chunks.push_back(AddChunkPrefixValue(2, "www.foo.com/malware.html")); 1472 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1473 database_->UpdateFinished(true); 1474 ASSERT_TRUE(base::GetFileInfo(filename, &after_info)); 1475 EXPECT_LT(before_info.last_modified, after_info.last_modified); 1476 1477 // Deleting a chunk updates the database file. 1478 ASSERT_TRUE(base::TouchFile(filename, old_last_modified, old_last_modified)); 1479 ASSERT_TRUE(base::GetFileInfo(filename, &before_info)); 1480 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1481 AddDelChunk(safe_browsing_util::kMalwareList, 2); 1482 database_->UpdateFinished(true); 1483 ASSERT_TRUE(base::GetFileInfo(filename, &after_info)); 1484 EXPECT_LT(before_info.last_modified, after_info.last_modified); 1485 1486 // Simply calling |UpdateStarted()| then |UpdateFinished()| does not 1487 // update the database file. 1488 ASSERT_TRUE(base::TouchFile(filename, old_last_modified, old_last_modified)); 1489 ASSERT_TRUE(base::GetFileInfo(filename, &before_info)); 1490 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1491 database_->UpdateFinished(true); 1492 ASSERT_TRUE(base::GetFileInfo(filename, &after_info)); 1493 EXPECT_EQ(before_info.last_modified, after_info.last_modified); 1494 } 1495 1496 // Test that a filter file is written out during update and read back 1497 // in during setup. 1498 TEST_F(SafeBrowsingDatabaseTest, FilterFile) { 1499 // Create a database with trivial example data and write it out. 1500 { 1501 // Prime the database. 1502 std::vector<SBListChunkRanges> lists; 1503 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1504 1505 ScopedVector<SBChunkData> chunks; 1506 chunks.push_back(AddChunkPrefixValue(1, "www.evil.com/malware.html")); 1507 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1508 database_->UpdateFinished(true); 1509 } 1510 1511 // Find the malware url in the database, don't find a good url. 1512 std::vector<SBPrefix> prefix_hits; 1513 std::vector<SBFullHashResult> cache_hits; 1514 EXPECT_TRUE(database_->ContainsBrowseUrl( 1515 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits)); 1516 EXPECT_FALSE(database_->ContainsBrowseUrl( 1517 GURL("http://www.good.com/goodware.html"), &prefix_hits, &cache_hits)); 1518 1519 base::FilePath filter_file = database_->PrefixSetForFilename( 1520 database_->BrowseDBFilename(database_filename_)); 1521 1522 // After re-creating the database, it should have a filter read from 1523 // a file, so it should find the same results. 1524 ASSERT_TRUE(base::PathExists(filter_file)); 1525 database_.reset(new SafeBrowsingDatabaseNew); 1526 database_->Init(database_filename_); 1527 EXPECT_TRUE(database_->ContainsBrowseUrl( 1528 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits)); 1529 EXPECT_FALSE(database_->ContainsBrowseUrl( 1530 GURL("http://www.good.com/goodware.html"), &prefix_hits, &cache_hits)); 1531 1532 // If there is no filter file, the database cannot find malware urls. 1533 base::DeleteFile(filter_file, false); 1534 ASSERT_FALSE(base::PathExists(filter_file)); 1535 database_.reset(new SafeBrowsingDatabaseNew); 1536 database_->Init(database_filename_); 1537 EXPECT_FALSE(database_->ContainsBrowseUrl( 1538 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits)); 1539 EXPECT_FALSE(database_->ContainsBrowseUrl( 1540 GURL("http://www.good.com/goodware.html"), &prefix_hits, &cache_hits)); 1541 } 1542 1543 TEST_F(SafeBrowsingDatabaseTest, MalwareIpBlacklist) { 1544 database_.reset(); 1545 SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); 1546 SafeBrowsingStoreFile* ip_blacklist_store = new SafeBrowsingStoreFile(); 1547 database_.reset(new SafeBrowsingDatabaseNew(browse_store, 1548 NULL, 1549 NULL, 1550 NULL, 1551 NULL, 1552 NULL, 1553 ip_blacklist_store)); 1554 database_->Init(database_filename_); 1555 std::vector<SBListChunkRanges> lists; 1556 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1557 1558 ScopedVector<SBChunkData> chunks; 1559 1560 // IPv4 prefix match for ::ffff:192.168.1.0/120. 1561 chunks.push_back(AddChunkHashedIpValue(1, "::ffff:192.168.1.0", 120)); 1562 1563 // IPv4 exact match for ::ffff:192.1.1.1. 1564 chunks.push_back(AddChunkHashedIpValue(2, "::ffff:192.1.1.1", 128)); 1565 1566 // IPv6 exact match for: fe80::31a:a0ff:fe10:786e/128. 1567 chunks.push_back(AddChunkHashedIpValue(3, "fe80::31a:a0ff:fe10:786e", 128)); 1568 1569 // IPv6 prefix match for: 2620:0:1000:3103::/64. 1570 chunks.push_back(AddChunkHashedIpValue(4, "2620:0:1000:3103::", 64)); 1571 1572 // IPv4 prefix match for ::ffff:192.1.122.0/119. 1573 chunks.push_back(AddChunkHashedIpValue(5, "::ffff:192.1.122.0", 119)); 1574 1575 // IPv4 prefix match for ::ffff:192.1.128.0/113. 1576 chunks.push_back(AddChunkHashedIpValue(6, "::ffff:192.1.128.0", 113)); 1577 1578 database_->InsertChunks(safe_browsing_util::kIPBlacklist, chunks.get()); 1579 database_->UpdateFinished(true); 1580 1581 EXPECT_FALSE(database_->ContainsMalwareIP("192.168.0.255")); 1582 EXPECT_TRUE(database_->ContainsMalwareIP("192.168.1.0")); 1583 EXPECT_TRUE(database_->ContainsMalwareIP("192.168.1.255")); 1584 EXPECT_TRUE(database_->ContainsMalwareIP("192.168.1.10")); 1585 EXPECT_TRUE(database_->ContainsMalwareIP("::ffff:192.168.1.2")); 1586 EXPECT_FALSE(database_->ContainsMalwareIP("192.168.2.0")); 1587 1588 EXPECT_FALSE(database_->ContainsMalwareIP("192.1.1.0")); 1589 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.1.1")); 1590 EXPECT_FALSE(database_->ContainsMalwareIP("192.1.1.2")); 1591 1592 EXPECT_FALSE(database_->ContainsMalwareIP( 1593 "2620:0:1000:3102:ffff:ffff:ffff:ffff")); 1594 EXPECT_TRUE(database_->ContainsMalwareIP("2620:0:1000:3103::")); 1595 EXPECT_TRUE(database_->ContainsMalwareIP( 1596 "2620:0:1000:3103:ffff:ffff:ffff:ffff")); 1597 EXPECT_FALSE(database_->ContainsMalwareIP("2620:0:1000:3104::")); 1598 1599 EXPECT_FALSE(database_->ContainsMalwareIP("fe80::21a:a0ff:fe10:786d")); 1600 EXPECT_TRUE(database_->ContainsMalwareIP("fe80::31a:a0ff:fe10:786e")); 1601 EXPECT_FALSE(database_->ContainsMalwareIP("fe80::21a:a0ff:fe10:786f")); 1602 1603 EXPECT_FALSE(database_->ContainsMalwareIP("192.1.121.255")); 1604 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.122.0")); 1605 EXPECT_TRUE(database_->ContainsMalwareIP("::ffff:192.1.122.1")); 1606 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.122.255")); 1607 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.123.0")); 1608 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.123.255")); 1609 EXPECT_FALSE(database_->ContainsMalwareIP("192.1.124.0")); 1610 1611 EXPECT_FALSE(database_->ContainsMalwareIP("192.1.127.255")); 1612 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.128.0")); 1613 EXPECT_TRUE(database_->ContainsMalwareIP("::ffff:192.1.128.1")); 1614 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.128.255")); 1615 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.255.0")); 1616 EXPECT_TRUE(database_->ContainsMalwareIP("192.1.255.255")); 1617 EXPECT_FALSE(database_->ContainsMalwareIP("192.2.0.0")); 1618 } 1619 1620 TEST_F(SafeBrowsingDatabaseTest, ContainsBrowseURL) { 1621 std::vector<SBListChunkRanges> lists; 1622 ASSERT_TRUE(database_->UpdateStarted(&lists)); 1623 1624 // Add a host-level hit. 1625 { 1626 ScopedVector<SBChunkData> chunks; 1627 chunks.push_back(AddChunkPrefixValue(1, "www.evil.com/")); 1628 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1629 } 1630 1631 // Add a specific fullhash. 1632 static const char kWhateverMalware[] = "www.whatever.com/malware.html"; 1633 { 1634 ScopedVector<SBChunkData> chunks; 1635 chunks.push_back(AddChunkFullHashValue(2, kWhateverMalware)); 1636 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1637 } 1638 1639 // Add a fullhash which has a prefix collision for a known url. 1640 static const char kExampleFine[] = "www.example.com/fine.html"; 1641 static const char kExampleCollision[] = 1642 "www.example.com/3123364814/malware.htm"; 1643 ASSERT_EQ(SBPrefixForString(kExampleFine), 1644 SBPrefixForString(kExampleCollision)); 1645 { 1646 ScopedVector<SBChunkData> chunks; 1647 chunks.push_back(AddChunkFullHashValue(3, kExampleCollision)); 1648 database_->InsertChunks(safe_browsing_util::kMalwareList, chunks.get()); 1649 } 1650 1651 database_->UpdateFinished(true); 1652 1653 std::vector<SBPrefix> prefix_hits; 1654 std::vector<SBFullHashResult> cache_hits; 1655 1656 // Anything will hit the host prefix. 1657 EXPECT_TRUE(database_->ContainsBrowseUrl( 1658 GURL("http://www.evil.com/malware.html"), &prefix_hits, &cache_hits)); 1659 ASSERT_EQ(1U, prefix_hits.size()); 1660 EXPECT_EQ(SBPrefixForString("www.evil.com/"), prefix_hits[0]); 1661 EXPECT_TRUE(cache_hits.empty()); 1662 1663 // Hit the specific URL prefix. 1664 EXPECT_TRUE(database_->ContainsBrowseUrl( 1665 GURL(std::string("http://") + kWhateverMalware), 1666 &prefix_hits, &cache_hits)); 1667 ASSERT_EQ(1U, prefix_hits.size()); 1668 EXPECT_EQ(SBPrefixForString(kWhateverMalware), prefix_hits[0]); 1669 EXPECT_TRUE(cache_hits.empty()); 1670 1671 // Other URLs at that host are fine. 1672 EXPECT_FALSE(database_->ContainsBrowseUrl( 1673 GURL("http://www.whatever.com/fine.html"), &prefix_hits, &cache_hits)); 1674 EXPECT_TRUE(prefix_hits.empty()); 1675 EXPECT_TRUE(cache_hits.empty()); 1676 1677 // Hit the specific URL full hash. 1678 EXPECT_TRUE(database_->ContainsBrowseUrl( 1679 GURL(std::string("http://") + kExampleCollision), 1680 &prefix_hits, &cache_hits)); 1681 ASSERT_EQ(1U, prefix_hits.size()); 1682 EXPECT_EQ(SBPrefixForString(kExampleCollision), prefix_hits[0]); 1683 EXPECT_TRUE(cache_hits.empty()); 1684 1685 // This prefix collides, but no full hash match. 1686 EXPECT_FALSE(database_->ContainsBrowseUrl( 1687 GURL(std::string("http://") + kExampleFine), &prefix_hits, &cache_hits)); 1688 } 1689