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