1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/net/sqlite_persistent_cookie_store.h"
6
7#include <map>
8#include <set>
9
10#include "base/bind.h"
11#include "base/callback.h"
12#include "base/file_util.h"
13#include "base/files/scoped_temp_dir.h"
14#include "base/memory/ref_counted.h"
15#include "base/message_loop/message_loop.h"
16#include "base/sequenced_task_runner.h"
17#include "base/stl_util.h"
18#include "base/synchronization/waitable_event.h"
19#include "base/test/sequenced_worker_pool_owner.h"
20#include "base/threading/sequenced_worker_pool.h"
21#include "base/time/time.h"
22#include "content/public/browser/cookie_crypto_delegate.h"
23#include "content/public/browser/cookie_store_factory.h"
24#include "crypto/encryptor.h"
25#include "crypto/symmetric_key.h"
26#include "net/cookies/canonical_cookie.h"
27#include "net/cookies/cookie_constants.h"
28#include "sql/connection.h"
29#include "sql/meta_table.h"
30#include "sql/statement.h"
31#include "testing/gtest/include/gtest/gtest.h"
32#include "url/gurl.h"
33
34namespace content {
35
36namespace {
37
38const base::FilePath::CharType kCookieFilename[] = FILE_PATH_LITERAL("Cookies");
39
40class CookieCryptor : public content::CookieCryptoDelegate {
41 public:
42  CookieCryptor();
43  virtual bool EncryptString(const std::string& plaintext,
44                             std::string* ciphertext) OVERRIDE;
45  virtual bool DecryptString(const std::string& ciphertext,
46                             std::string* plaintext) OVERRIDE;
47
48 private:
49  scoped_ptr<crypto::SymmetricKey> key_;
50  crypto::Encryptor encryptor_;
51};
52
53CookieCryptor::CookieCryptor() : key_(
54    crypto::SymmetricKey::DeriveKeyFromPassword(
55        crypto::SymmetricKey::AES, "password", "saltiest", 1000, 256)) {
56  std::string iv("the iv: 16 bytes");
57  encryptor_.Init(key_.get(), crypto::Encryptor::CBC, iv);
58}
59
60bool CookieCryptor::EncryptString(const std::string& plaintext,
61                                  std::string* ciphertext) {
62  return encryptor_.Encrypt(plaintext, ciphertext);
63}
64
65bool CookieCryptor::DecryptString(const std::string& ciphertext,
66                                  std::string* plaintext) {
67  return encryptor_.Decrypt(ciphertext, plaintext);
68}
69
70}  // namespace
71
72typedef std::vector<net::CanonicalCookie*> CanonicalCookieVector;
73
74class SQLitePersistentCookieStoreTest : public testing::Test {
75 public:
76  SQLitePersistentCookieStoreTest()
77      : pool_owner_(new base::SequencedWorkerPoolOwner(3, "Background Pool")),
78        loaded_event_(false, false),
79        key_loaded_event_(false, false),
80        db_thread_event_(false, false) {
81  }
82
83  void OnLoaded(const CanonicalCookieVector& cookies) {
84    cookies_ = cookies;
85    loaded_event_.Signal();
86  }
87
88  void OnKeyLoaded(const CanonicalCookieVector& cookies) {
89    cookies_ = cookies;
90    key_loaded_event_.Signal();
91  }
92
93  void Load(CanonicalCookieVector* cookies) {
94    EXPECT_FALSE(loaded_event_.IsSignaled());
95    store_->Load(base::Bind(&SQLitePersistentCookieStoreTest::OnLoaded,
96                            base::Unretained(this)));
97    loaded_event_.Wait();
98    *cookies = cookies_;
99  }
100
101  void Flush() {
102    base::WaitableEvent event(false, false);
103    store_->Flush(base::Bind(&base::WaitableEvent::Signal,
104                             base::Unretained(&event)));
105    event.Wait();
106  }
107
108  scoped_refptr<base::SequencedTaskRunner> background_task_runner() {
109    return pool_owner_->pool()->GetSequencedTaskRunner(
110        pool_owner_->pool()->GetNamedSequenceToken("background"));
111  }
112
113  scoped_refptr<base::SequencedTaskRunner> client_task_runner() {
114    return pool_owner_->pool()->GetSequencedTaskRunner(
115        pool_owner_->pool()->GetNamedSequenceToken("client"));
116  }
117
118  void DestroyStore() {
119    store_ = NULL;
120    // Make sure we wait until the destructor has run by shutting down the pool
121    // resetting the owner (whose destructor blocks on the pool completion).
122    pool_owner_->pool()->Shutdown();
123    // Create a new pool for the few tests that create multiple stores. In other
124    // cases this is wasted but harmless.
125    pool_owner_.reset(new base::SequencedWorkerPoolOwner(3, "Background Pool"));
126  }
127
128  void CreateAndLoad(bool crypt_cookies,
129                     bool restore_old_session_cookies,
130                     CanonicalCookieVector* cookies) {
131    store_ = new SQLitePersistentCookieStore(
132        temp_dir_.path().Append(kCookieFilename),
133        client_task_runner(),
134        background_task_runner(),
135        restore_old_session_cookies,
136        NULL,
137        crypt_cookies ?
138            scoped_ptr<content::CookieCryptoDelegate>(new CookieCryptor) :
139            scoped_ptr<content::CookieCryptoDelegate>());
140    Load(cookies);
141  }
142
143  void InitializeStore(bool crypt, bool restore_old_session_cookies) {
144    CanonicalCookieVector cookies;
145    CreateAndLoad(crypt, restore_old_session_cookies, &cookies);
146    EXPECT_EQ(0U, cookies.size());
147  }
148
149  // We have to create this method to wrap WaitableEvent::Wait, since we cannot
150  // bind a non-void returning method as a Closure.
151  void WaitOnDBEvent() {
152    db_thread_event_.Wait();
153  }
154
155  // Adds a persistent cookie to store_.
156  void AddCookie(const std::string& name,
157                 const std::string& value,
158                 const std::string& domain,
159                 const std::string& path,
160                 const base::Time& creation) {
161    store_->AddCookie(
162        net::CanonicalCookie(GURL(), name, value, domain, path, creation,
163                             creation, creation, false, false,
164                             net::COOKIE_PRIORITY_DEFAULT));
165  }
166
167  std::string ReadRawDBContents() {
168    std::string contents;
169    if (!base::ReadFileToString(temp_dir_.path().Append(kCookieFilename),
170                                &contents))
171      return std::string();
172    return contents;
173  }
174
175  virtual void SetUp() OVERRIDE {
176    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
177  }
178
179  virtual void TearDown() OVERRIDE {
180    DestroyStore();
181    pool_owner_->pool()->Shutdown();
182  }
183
184 protected:
185  base::MessageLoop main_loop_;
186  scoped_ptr<base::SequencedWorkerPoolOwner> pool_owner_;
187  base::WaitableEvent loaded_event_;
188  base::WaitableEvent key_loaded_event_;
189  base::WaitableEvent db_thread_event_;
190  CanonicalCookieVector cookies_;
191  base::ScopedTempDir temp_dir_;
192  scoped_refptr<SQLitePersistentCookieStore> store_;
193};
194
195TEST_F(SQLitePersistentCookieStoreTest, TestInvalidMetaTableRecovery) {
196  InitializeStore(false, false);
197  AddCookie("A", "B", "foo.bar", "/", base::Time::Now());
198  DestroyStore();
199
200  // Load up the store and verify that it has good data in it.
201  CanonicalCookieVector cookies;
202  CreateAndLoad(false, false, &cookies);
203  ASSERT_EQ(1U, cookies.size());
204  ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str());
205  ASSERT_STREQ("A", cookies[0]->Name().c_str());
206  ASSERT_STREQ("B", cookies[0]->Value().c_str());
207  DestroyStore();
208  STLDeleteElements(&cookies);
209
210  // Now corrupt the meta table.
211  {
212    sql::Connection db;
213    ASSERT_TRUE(db.Open(temp_dir_.path().Append(kCookieFilename)));
214    sql::MetaTable meta_table_;
215    meta_table_.Init(&db, 1, 1);
216    ASSERT_TRUE(db.Execute("DELETE FROM meta"));
217    db.Close();
218  }
219
220  // Upon loading, the database should be reset to a good, blank state.
221  CreateAndLoad(false, false, &cookies);
222  ASSERT_EQ(0U, cookies.size());
223
224  // Verify that, after, recovery, the database persists properly.
225  AddCookie("X", "Y", "foo.bar", "/", base::Time::Now());
226  DestroyStore();
227  CreateAndLoad(false, false, &cookies);
228  ASSERT_EQ(1U, cookies.size());
229  ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str());
230  ASSERT_STREQ("X", cookies[0]->Name().c_str());
231  ASSERT_STREQ("Y", cookies[0]->Value().c_str());
232  STLDeleteElements(&cookies);
233}
234
235// Test if data is stored as expected in the SQLite database.
236TEST_F(SQLitePersistentCookieStoreTest, TestPersistance) {
237  InitializeStore(false, false);
238  AddCookie("A", "B", "foo.bar", "/", base::Time::Now());
239  // Replace the store effectively destroying the current one and forcing it
240  // to write its data to disk. Then we can see if after loading it again it
241  // is still there.
242  DestroyStore();
243  // Reload and test for persistence
244  CanonicalCookieVector cookies;
245  CreateAndLoad(false, false, &cookies);
246  ASSERT_EQ(1U, cookies.size());
247  ASSERT_STREQ("foo.bar", cookies[0]->Domain().c_str());
248  ASSERT_STREQ("A", cookies[0]->Name().c_str());
249  ASSERT_STREQ("B", cookies[0]->Value().c_str());
250
251  // Now delete the cookie and check persistence again.
252  store_->DeleteCookie(*cookies[0]);
253  DestroyStore();
254  STLDeleteElements(&cookies);
255
256  // Reload and check if the cookie has been removed.
257  CreateAndLoad(false, false, &cookies);
258  ASSERT_EQ(0U, cookies.size());
259}
260
261// Test that priority load of cookies for a specfic domain key could be
262// completed before the entire store is loaded
263TEST_F(SQLitePersistentCookieStoreTest, TestLoadCookiesForKey) {
264  InitializeStore(false, false);
265  base::Time t = base::Time::Now();
266  AddCookie("A", "B", "foo.bar", "/", t);
267  t += base::TimeDelta::FromInternalValue(10);
268  AddCookie("A", "B", "www.aaa.com", "/", t);
269  t += base::TimeDelta::FromInternalValue(10);
270  AddCookie("A", "B", "travel.aaa.com", "/", t);
271  t += base::TimeDelta::FromInternalValue(10);
272  AddCookie("A", "B", "www.bbb.com", "/", t);
273  DestroyStore();
274
275  store_ = new SQLitePersistentCookieStore(
276      temp_dir_.path().Append(kCookieFilename),
277      client_task_runner(),
278      background_task_runner(),
279      false, NULL,
280      scoped_ptr<content::CookieCryptoDelegate>());
281
282  // Posting a blocking task to db_thread_ makes sure that the DB thread waits
283  // until both Load and LoadCookiesForKey have been posted to its task queue.
284  background_task_runner()->PostTask(
285      FROM_HERE,
286      base::Bind(&SQLitePersistentCookieStoreTest::WaitOnDBEvent,
287                 base::Unretained(this)));
288  store_->Load(base::Bind(&SQLitePersistentCookieStoreTest::OnLoaded,
289                          base::Unretained(this)));
290  store_->LoadCookiesForKey("aaa.com",
291    base::Bind(&SQLitePersistentCookieStoreTest::OnKeyLoaded,
292               base::Unretained(this)));
293  background_task_runner()->PostTask(
294      FROM_HERE,
295      base::Bind(&SQLitePersistentCookieStoreTest::WaitOnDBEvent,
296                 base::Unretained(this)));
297
298  // Now the DB-thread queue contains:
299  // (active:)
300  // 1. Wait (on db_event)
301  // (pending:)
302  // 2. "Init And Chain-Load First Domain"
303  // 3. Priority Load (aaa.com)
304  // 4. Wait (on db_event)
305  db_thread_event_.Signal();
306  key_loaded_event_.Wait();
307  ASSERT_EQ(loaded_event_.IsSignaled(), false);
308  std::set<std::string> cookies_loaded;
309  for (CanonicalCookieVector::const_iterator it = cookies_.begin();
310       it != cookies_.end();
311       ++it) {
312    cookies_loaded.insert((*it)->Domain().c_str());
313  }
314  STLDeleteElements(&cookies_);
315  ASSERT_GT(4U, cookies_loaded.size());
316  ASSERT_EQ(true, cookies_loaded.find("www.aaa.com") != cookies_loaded.end());
317  ASSERT_EQ(true,
318            cookies_loaded.find("travel.aaa.com") != cookies_loaded.end());
319
320  db_thread_event_.Signal();
321  loaded_event_.Wait();
322  for (CanonicalCookieVector::const_iterator it = cookies_.begin();
323       it != cookies_.end();
324       ++it) {
325    cookies_loaded.insert((*it)->Domain().c_str());
326  }
327  ASSERT_EQ(4U, cookies_loaded.size());
328  ASSERT_EQ(cookies_loaded.find("foo.bar") != cookies_loaded.end(),
329            true);
330  ASSERT_EQ(cookies_loaded.find("www.bbb.com") != cookies_loaded.end(), true);
331  STLDeleteElements(&cookies_);
332}
333
334// Test that we can force the database to be written by calling Flush().
335TEST_F(SQLitePersistentCookieStoreTest, TestFlush) {
336  InitializeStore(false, false);
337  // File timestamps don't work well on all platforms, so we'll determine
338  // whether the DB file has been modified by checking its size.
339  base::FilePath path = temp_dir_.path().Append(kCookieFilename);
340  base::PlatformFileInfo info;
341  ASSERT_TRUE(base::GetFileInfo(path, &info));
342  int64 base_size = info.size;
343
344  // Write some large cookies, so the DB will have to expand by several KB.
345  for (char c = 'a'; c < 'z'; ++c) {
346    // Each cookie needs a unique timestamp for creation_utc (see DB schema).
347    base::Time t = base::Time::Now() + base::TimeDelta::FromMicroseconds(c);
348    std::string name(1, c);
349    std::string value(1000, c);
350    AddCookie(name, value, "foo.bar", "/", t);
351  }
352
353  Flush();
354
355  // We forced a write, so now the file will be bigger.
356  ASSERT_TRUE(base::GetFileInfo(path, &info));
357  ASSERT_GT(info.size, base_size);
358}
359
360// Test loading old session cookies from the disk.
361TEST_F(SQLitePersistentCookieStoreTest, TestLoadOldSessionCookies) {
362  InitializeStore(false, true);
363
364  // Add a session cookie.
365  store_->AddCookie(
366      net::CanonicalCookie(
367          GURL(), "C", "D", "sessioncookie.com", "/", base::Time::Now(),
368          base::Time(), base::Time::Now(), false, false,
369          net::COOKIE_PRIORITY_DEFAULT));
370
371  // Force the store to write its data to the disk.
372  DestroyStore();
373
374  // Create a store that loads session cookies and test that the session cookie
375  // was loaded.
376  CanonicalCookieVector cookies;
377  CreateAndLoad(false, true, &cookies);
378
379  ASSERT_EQ(1U, cookies.size());
380  ASSERT_STREQ("sessioncookie.com", cookies[0]->Domain().c_str());
381  ASSERT_STREQ("C", cookies[0]->Name().c_str());
382  ASSERT_STREQ("D", cookies[0]->Value().c_str());
383  ASSERT_EQ(net::COOKIE_PRIORITY_DEFAULT, cookies[0]->Priority());
384
385  STLDeleteElements(&cookies);
386}
387
388// Test loading old session cookies from the disk.
389TEST_F(SQLitePersistentCookieStoreTest, TestDontLoadOldSessionCookies) {
390  InitializeStore(false, true);
391
392  // Add a session cookie.
393  store_->AddCookie(
394      net::CanonicalCookie(
395          GURL(), "C", "D", "sessioncookie.com", "/", base::Time::Now(),
396          base::Time(), base::Time::Now(), false, false,
397          net::COOKIE_PRIORITY_DEFAULT));
398
399  // Force the store to write its data to the disk.
400  DestroyStore();
401
402  // Create a store that doesn't load old session cookies and test that the
403  // session cookie was not loaded.
404  CanonicalCookieVector cookies;
405  CreateAndLoad(false, false, &cookies);
406  ASSERT_EQ(0U, cookies.size());
407
408  // The store should also delete the session cookie. Wait until that has been
409  // done.
410  DestroyStore();
411
412  // Create a store that loads old session cookies and test that the session
413  // cookie is gone.
414  CreateAndLoad(false, true, &cookies);
415  ASSERT_EQ(0U, cookies.size());
416}
417
418TEST_F(SQLitePersistentCookieStoreTest, PersistIsPersistent) {
419  InitializeStore(false, true);
420  static const char kSessionName[] = "session";
421  static const char kPersistentName[] = "persistent";
422
423  // Add a session cookie.
424  store_->AddCookie(
425      net::CanonicalCookie(
426          GURL(), kSessionName, "val", "sessioncookie.com", "/",
427          base::Time::Now(), base::Time(), base::Time::Now(), false, false,
428          net::COOKIE_PRIORITY_DEFAULT));
429  // Add a persistent cookie.
430  store_->AddCookie(
431      net::CanonicalCookie(
432          GURL(), kPersistentName, "val", "sessioncookie.com", "/",
433          base::Time::Now() - base::TimeDelta::FromDays(1),
434          base::Time::Now() + base::TimeDelta::FromDays(1),
435          base::Time::Now(), false, false,
436          net::COOKIE_PRIORITY_DEFAULT));
437
438  // Force the store to write its data to the disk.
439  DestroyStore();
440
441  // Create a store that loads session cookie and test that the IsPersistent
442  // attribute is restored.
443  CanonicalCookieVector cookies;
444  CreateAndLoad(false, true, &cookies);
445  ASSERT_EQ(2U, cookies.size());
446
447  std::map<std::string, net::CanonicalCookie*> cookie_map;
448  for (CanonicalCookieVector::const_iterator it = cookies.begin();
449       it != cookies.end();
450       ++it) {
451    cookie_map[(*it)->Name()] = *it;
452  }
453
454  std::map<std::string, net::CanonicalCookie*>::const_iterator it =
455      cookie_map.find(kSessionName);
456  ASSERT_TRUE(it != cookie_map.end());
457  EXPECT_FALSE(cookie_map[kSessionName]->IsPersistent());
458
459  it = cookie_map.find(kPersistentName);
460  ASSERT_TRUE(it != cookie_map.end());
461  EXPECT_TRUE(cookie_map[kPersistentName]->IsPersistent());
462
463  STLDeleteElements(&cookies);
464}
465
466TEST_F(SQLitePersistentCookieStoreTest, PriorityIsPersistent) {
467  static const char kLowName[] = "low";
468  static const char kMediumName[] = "medium";
469  static const char kHighName[] = "high";
470  static const char kCookieDomain[] = "sessioncookie.com";
471  static const char kCookieValue[] = "value";
472  static const char kCookiePath[] = "/";
473
474  InitializeStore(false, true);
475
476  // Add a low-priority persistent cookie.
477  store_->AddCookie(
478      net::CanonicalCookie(
479          GURL(), kLowName, kCookieValue, kCookieDomain, kCookiePath,
480          base::Time::Now() - base::TimeDelta::FromMinutes(1),
481          base::Time::Now() + base::TimeDelta::FromDays(1),
482          base::Time::Now(), false, false,
483          net::COOKIE_PRIORITY_LOW));
484
485  // Add a medium-priority persistent cookie.
486  store_->AddCookie(
487      net::CanonicalCookie(
488          GURL(), kMediumName, kCookieValue, kCookieDomain, kCookiePath,
489          base::Time::Now() - base::TimeDelta::FromMinutes(2),
490          base::Time::Now() + base::TimeDelta::FromDays(1),
491          base::Time::Now(), false, false,
492          net::COOKIE_PRIORITY_MEDIUM));
493
494  // Add a high-priority peristent cookie.
495  store_->AddCookie(
496      net::CanonicalCookie(
497          GURL(), kHighName, kCookieValue, kCookieDomain, kCookiePath,
498          base::Time::Now() - base::TimeDelta::FromMinutes(3),
499          base::Time::Now() + base::TimeDelta::FromDays(1),
500          base::Time::Now(), false, false,
501          net::COOKIE_PRIORITY_HIGH));
502
503  // Force the store to write its data to the disk.
504  DestroyStore();
505
506  // Create a store that loads session cookie and test that the priority
507  // attribute values are restored.
508  CanonicalCookieVector cookies;
509  CreateAndLoad(false, true, &cookies);
510  ASSERT_EQ(3U, cookies.size());
511
512  // Put the cookies into a map, by name, so we can easily find them.
513  std::map<std::string, net::CanonicalCookie*> cookie_map;
514  for (CanonicalCookieVector::const_iterator it = cookies.begin();
515       it != cookies.end();
516       ++it) {
517    cookie_map[(*it)->Name()] = *it;
518  }
519
520  // Validate that each cookie has the correct priority.
521  std::map<std::string, net::CanonicalCookie*>::const_iterator it =
522      cookie_map.find(kLowName);
523  ASSERT_TRUE(it != cookie_map.end());
524  EXPECT_EQ(net::COOKIE_PRIORITY_LOW, cookie_map[kLowName]->Priority());
525
526  it = cookie_map.find(kMediumName);
527  ASSERT_TRUE(it != cookie_map.end());
528  EXPECT_EQ(net::COOKIE_PRIORITY_MEDIUM, cookie_map[kMediumName]->Priority());
529
530  it = cookie_map.find(kHighName);
531  ASSERT_TRUE(it != cookie_map.end());
532  EXPECT_EQ(net::COOKIE_PRIORITY_HIGH, cookie_map[kHighName]->Priority());
533
534  STLDeleteElements(&cookies);
535}
536
537TEST_F(SQLitePersistentCookieStoreTest, UpdateToEncryption) {
538  CanonicalCookieVector cookies;
539
540  // Create unencrypted cookie store and write something to it.
541  InitializeStore(false, false);
542  AddCookie("name", "value123XYZ", "foo.bar", "/", base::Time::Now());
543  DestroyStore();
544
545  // Verify that "value" is visible in the file.  This is necessary in order to
546  // have confidence in a later test that "encrypted_value" is not visible.
547  std::string contents = ReadRawDBContents();
548  EXPECT_NE(0U, contents.length());
549  EXPECT_NE(contents.find("value123XYZ"), std::string::npos);
550
551  // Create encrypted cookie store and ensure old cookie still reads.
552  STLDeleteElements(&cookies_);
553  EXPECT_EQ(0U, cookies_.size());
554  CreateAndLoad(true, false, &cookies);
555  EXPECT_EQ(1U, cookies_.size());
556  EXPECT_EQ("name", cookies_[0]->Name());
557  EXPECT_EQ("value123XYZ", cookies_[0]->Value());
558
559  // Make sure we can update existing cookie and add new cookie as encrypted.
560  store_->DeleteCookie(*(cookies_[0]));
561  AddCookie("name", "encrypted_value123XYZ", "foo.bar", "/", base::Time::Now());
562  AddCookie("other", "something456ABC", "foo.bar", "/",
563            base::Time::Now() + base::TimeDelta::FromInternalValue(10));
564  DestroyStore();
565  STLDeleteElements(&cookies_);
566  CreateAndLoad(true, false, &cookies);
567  EXPECT_EQ(2U, cookies_.size());
568  net::CanonicalCookie* cookie_name = NULL;
569  net::CanonicalCookie* cookie_other = NULL;
570  if (cookies_[0]->Name() == "name") {
571    cookie_name = cookies_[0];
572    cookie_other = cookies_[1];
573  } else {
574    cookie_name = cookies_[1];
575    cookie_other = cookies_[0];
576  }
577  EXPECT_EQ("encrypted_value123XYZ", cookie_name->Value());
578  EXPECT_EQ("something456ABC", cookie_other->Value());
579  DestroyStore();
580  STLDeleteElements(&cookies_);
581
582  // Examine the real record to make sure plaintext version doesn't exist.
583  sql::Connection db;
584  sql::Statement smt;
585  int resultcount = 0;
586  ASSERT_TRUE(db.Open(temp_dir_.path().Append(kCookieFilename)));
587  smt.Assign(db.GetCachedStatement(SQL_FROM_HERE,
588                                   "SELECT * "
589                                   "FROM cookies "
590                                   "WHERE host_key = 'foo.bar'"));
591  while (smt.Step()) {
592    resultcount++;
593    for (int i=0; i < smt.ColumnCount(); i++) {
594      EXPECT_EQ(smt.ColumnString(i).find("value"), std::string::npos);
595      EXPECT_EQ(smt.ColumnString(i).find("something"), std::string::npos);
596    }
597  }
598  EXPECT_EQ(2, resultcount);
599
600  // Verify that "encrypted_value" is NOT visible in the file.
601  contents = ReadRawDBContents();
602  EXPECT_NE(0U, contents.length());
603  EXPECT_EQ(contents.find("encrypted_value123XYZ"), std::string::npos);
604  EXPECT_EQ(contents.find("something456ABC"), std::string::npos);
605}
606
607}  // namespace content
608