• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Sha256Prefix(const std::string & str)27 SBPrefix Sha256Prefix(const std::string& str) {
28   SBPrefix prefix;
29   crypto::SHA256HashString(str, &prefix, sizeof(prefix));
30   return prefix;
31 }
32 
Sha256Hash(const std::string & str)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.
InsertAddChunkHostPrefixValue(SBChunk * chunk,int chunk_number,const SBPrefix & host_prefix,const SBPrefix & url_prefix)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.
InsertAddChunkHostPrefixUrl(SBChunk * chunk,int chunk_number,const std::string & host_name,const std::string & url)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.
InsertAddChunkHostFullHashes(SBChunk * chunk,int chunk_number,const std::string & host_name,const std::string & url)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.
InsertAddChunkHost2PrefixUrls(SBChunk * chunk,int chunk_number,const std::string & host_name,const std::string & url1,const std::string & url2)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.
InsertAddChunkHost2FullHashes(SBChunk * chunk,int chunk_number,const std::string & host_name,const std::string & url1,const std::string & url2)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.
InsertSubChunkHostPrefixValue(SBChunk * chunk,int chunk_number,int chunk_id_to_sub,const SBPrefix & host_prefix,const SBPrefix & url_prefix)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.
InsertSubChunkHostPrefixUrl(SBChunk * chunk,int chunk_number,int chunk_id_to_sub,const std::string & host_name,const std::string & url)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.
InsertSubChunkHost2PrefixUrls(SBChunk * chunk,int chunk_number,int chunk_id_to_sub,const std::string & host_name,const std::string & url1,const std::string & url2)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.
InsertSubChunkHost2FullHashes(SBChunk * chunk,int chunk_number,int chunk_id_to_sub,const std::string & host_name,const std::string & url1,const std::string & url2)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.
InsertSubChunkHostFullHash(SBChunk * chunk,int chunk_number,int chunk_id_to_sub,const std::string & host_name,const std::string & url)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:
ScopedLogMessageIgnorer()208   ScopedLogMessageIgnorer() {
209     logging::SetLogMessageHandler(&LogMessageIgnorer);
210   }
~ScopedLogMessageIgnorer()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:
LogMessageIgnorer(int severity,const char * file,int line,size_t message_start,const std::string & str)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:
SetUp()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 
TearDown()251   virtual void TearDown() {
252     database_.reset();
253 
254     PlatformTest::TearDown();
255   }
256 
GetListsInfo(std::vector<SBListChunkRanges> * lists)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.
DelChunk(const std::string & list,int chunk_id,bool is_sub_del)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 
AddDelChunk(const std::string & list,int chunk_id)276   void AddDelChunk(const std::string& list, int chunk_id) {
277     DelChunk(list, chunk_id, false);
278   }
279 
SubDelChunk(const std::string & list,int chunk_id)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.
TEST_F(SafeBrowsingDatabaseTest,ListNameForBrowse)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 
TEST_F(SafeBrowsingDatabaseTest,ListNameForBrowseAndDownload)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.
TEST_F(SafeBrowsingDatabaseTest,BrowseDatabase)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.
TEST_F(SafeBrowsingDatabaseTest,ZeroSizeChunk)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.
PopulateDatabaseForCacheTest()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 
TEST_F(SafeBrowsingDatabaseTest,HashCaching)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
TEST_F(SafeBrowsingDatabaseTest,DISABLED_FileCorruptionHandling)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.
TEST_F(SafeBrowsingDatabaseTest,ContainsDownloadUrl)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.
TEST_F(SafeBrowsingDatabaseTest,CsdWhitelist)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.
TEST_F(SafeBrowsingDatabaseTest,SameHostEntriesOkay)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 
TEST_F(SafeBrowsingDatabaseTest,BinHashInsertLookup)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.
TEST_F(SafeBrowsingDatabaseTest,EmptyUpdate)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