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// This test uses the safebrowsing test server published at
6// http://code.google.com/p/google-safe-browsing/ to test the safebrowsing
7// protocol implemetation. Details of the safebrowsing testing flow is
8// documented at
9// http://code.google.com/p/google-safe-browsing/wiki/ProtocolTesting
10//
11// This test launches safebrowsing test server and issues several update
12// requests against that server. Each update would get different data and after
13// each update, the test will get a list of URLs from the test server to verify
14// its repository. The test will succeed only if all updates are performed and
15// URLs match what the server expected.
16
17#include <vector>
18
19#include "base/bind.h"
20#include "base/command_line.h"
21#include "base/environment.h"
22#include "base/path_service.h"
23#include "base/strings/string_number_conversions.h"
24#include "base/strings/string_split.h"
25#include "base/strings/stringprintf.h"
26#include "base/strings/utf_string_conversions.h"
27#include "base/synchronization/lock.h"
28#include "base/test/test_timeouts.h"
29#include "base/threading/platform_thread.h"
30#include "base/threading/thread.h"
31#include "base/time/time.h"
32#include "chrome/browser/browser_process.h"
33#include "chrome/browser/chrome_notification_types.h"
34#include "chrome/browser/profiles/profile.h"
35#include "chrome/browser/safe_browsing/database_manager.h"
36#include "chrome/browser/safe_browsing/local_safebrowsing_test_server.h"
37#include "chrome/browser/safe_browsing/protocol_manager.h"
38#include "chrome/browser/safe_browsing/safe_browsing_service.h"
39#include "chrome/browser/ui/browser.h"
40#include "chrome/common/chrome_switches.h"
41#include "chrome/common/url_constants.h"
42#include "chrome/test/base/in_process_browser_test.h"
43#include "chrome/test/base/ui_test_utils.h"
44#include "content/public/browser/browser_context.h"
45#include "content/public/test/test_browser_thread.h"
46#include "net/base/load_flags.h"
47#include "net/base/net_log.h"
48#include "net/dns/host_resolver.h"
49#include "net/test/python_utils.h"
50#include "net/url_request/url_fetcher.h"
51#include "net/url_request/url_fetcher_delegate.h"
52#include "net/url_request/url_request_status.h"
53#include "testing/gtest/include/gtest/gtest.h"
54
55using content::BrowserThread;
56
57namespace {
58
59const base::FilePath::CharType kDataFile[] =
60    FILE_PATH_LITERAL("testing_input_nomac.dat");
61const char kUrlVerifyPath[] = "safebrowsing/verify_urls";
62const char kDBVerifyPath[] = "safebrowsing/verify_database";
63const char kTestCompletePath[] = "test_complete";
64
65struct PhishingUrl {
66  std::string url;
67  std::string list_name;
68  bool is_phishing;
69};
70
71// Parses server response for verify_urls. The expected format is:
72//
73// first.random.url.com/   internal-test-shavar   yes
74// second.random.url.com/  internal-test-shavar   yes
75// ...
76bool ParsePhishingUrls(const std::string& data,
77                       std::vector<PhishingUrl>* phishing_urls) {
78  if (data.empty())
79    return false;
80
81  std::vector<std::string> urls;
82  base::SplitString(data, '\n', &urls);
83  for (size_t i = 0; i < urls.size(); ++i) {
84    if (urls[i].empty())
85      continue;
86    PhishingUrl phishing_url;
87    std::vector<std::string> record_parts;
88    base::SplitString(urls[i], '\t', &record_parts);
89    if (record_parts.size() != 3) {
90      LOG(ERROR) << "Unexpected URL format in phishing URL list: "
91                 << urls[i];
92      return false;
93    }
94    phishing_url.url = std::string(url::kHttpScheme) + "://" + record_parts[0];
95    phishing_url.list_name = record_parts[1];
96    if (record_parts[2] == "yes") {
97      phishing_url.is_phishing = true;
98    } else if (record_parts[2] == "no") {
99      phishing_url.is_phishing = false;
100    } else {
101      LOG(ERROR) << "Unrecognized expectation in " << urls[i]
102                 << ": " << record_parts[2];
103      return false;
104    }
105    phishing_urls->push_back(phishing_url);
106  }
107  return true;
108}
109
110class FakeSafeBrowsingService : public SafeBrowsingService {
111 public:
112  explicit FakeSafeBrowsingService(const std::string& url_prefix)
113      : url_prefix_(url_prefix) {}
114
115  virtual SafeBrowsingProtocolConfig GetProtocolConfig() const OVERRIDE {
116    SafeBrowsingProtocolConfig config;
117    config.url_prefix = url_prefix_;
118    // Makes sure the auto update is not triggered. The tests will force the
119    // update when needed.
120    config.disable_auto_update = true;
121#if defined(OS_ANDROID)
122    config.disable_connection_check = true;
123#endif
124    config.client_name = "browser_tests";
125    return config;
126  }
127
128 private:
129  virtual ~FakeSafeBrowsingService() {}
130
131  std::string url_prefix_;
132
133  DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService);
134};
135
136// Factory that creates FakeSafeBrowsingService instances.
137class TestSafeBrowsingServiceFactory : public SafeBrowsingServiceFactory {
138 public:
139  explicit TestSafeBrowsingServiceFactory(const std::string& url_prefix)
140      : url_prefix_(url_prefix) {}
141
142  virtual SafeBrowsingService* CreateSafeBrowsingService() OVERRIDE {
143    return new FakeSafeBrowsingService(url_prefix_);
144  }
145
146 private:
147  std::string url_prefix_;
148};
149
150}  // namespace
151
152// This starts the browser and keeps status of states related to SafeBrowsing.
153class SafeBrowsingServerTest : public InProcessBrowserTest {
154 public:
155  SafeBrowsingServerTest()
156    : safe_browsing_service_(NULL),
157      is_database_ready_(true),
158      is_update_scheduled_(false),
159      is_checked_url_in_db_(false),
160      is_checked_url_safe_(false) {
161  }
162
163  virtual ~SafeBrowsingServerTest() {
164  }
165
166  void UpdateSafeBrowsingStatus() {
167    ASSERT_TRUE(safe_browsing_service_);
168    base::AutoLock lock(update_status_mutex_);
169    last_update_ = safe_browsing_service_->protocol_manager_->last_update();
170    is_update_scheduled_ =
171        safe_browsing_service_->protocol_manager_->update_timer_.IsRunning();
172  }
173
174  void ForceUpdate() {
175    content::WindowedNotificationObserver observer(
176        chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
177        content::Source<SafeBrowsingDatabaseManager>(database_manager()));
178    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
179        base::Bind(&SafeBrowsingServerTest::ForceUpdateOnIOThread,
180                   this));
181    observer.Wait();
182  }
183
184  void ForceUpdateOnIOThread() {
185    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
186    ASSERT_TRUE(safe_browsing_service_);
187    safe_browsing_service_->protocol_manager_->ForceScheduleNextUpdate(
188        base::TimeDelta::FromSeconds(0));
189  }
190
191  void CheckIsDatabaseReady() {
192    base::AutoLock lock(update_status_mutex_);
193    is_database_ready_ = !database_manager()->database_update_in_progress_;
194  }
195
196  void CheckUrl(SafeBrowsingDatabaseManager::Client* helper, const GURL& url) {
197    ASSERT_TRUE(safe_browsing_service_);
198    base::AutoLock lock(update_status_mutex_);
199    if (database_manager()->CheckBrowseUrl(url, helper)) {
200      is_checked_url_in_db_ = false;
201      is_checked_url_safe_ = true;
202    } else {
203      // In this case, Safebrowsing service will fetch the full hash
204      // from the server and examine that. Once it is done,
205      // set_is_checked_url_safe() will be called via callback.
206      is_checked_url_in_db_ = true;
207    }
208  }
209
210  SafeBrowsingDatabaseManager* database_manager() {
211    return safe_browsing_service_->database_manager().get();
212  }
213
214  bool is_checked_url_in_db() {
215    base::AutoLock l(update_status_mutex_);
216    return is_checked_url_in_db_;
217  }
218
219  void set_is_checked_url_safe(bool safe) {
220    base::AutoLock l(update_status_mutex_);
221    is_checked_url_safe_ = safe;
222  }
223
224  bool is_checked_url_safe() {
225    base::AutoLock l(update_status_mutex_);
226    return is_checked_url_safe_;
227  }
228
229  bool is_database_ready() {
230    base::AutoLock l(update_status_mutex_);
231    return is_database_ready_;
232  }
233
234  base::Time last_update() {
235    base::AutoLock l(update_status_mutex_);
236    return last_update_;
237  }
238
239  bool is_update_scheduled() {
240    base::AutoLock l(update_status_mutex_);
241    return is_update_scheduled_;
242  }
243
244  base::MessageLoop* SafeBrowsingMessageLoop() {
245    return database_manager()->safe_browsing_thread_->message_loop();
246  }
247
248  const net::SpawnedTestServer& test_server() const {
249    return *test_server_;
250  }
251
252 protected:
253  bool InitSafeBrowsingService() {
254    safe_browsing_service_ = g_browser_process->safe_browsing_service();
255    return safe_browsing_service_ != NULL;
256  }
257
258  virtual void SetUp() OVERRIDE {
259    base::FilePath datafile_path;
260    ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &datafile_path));
261
262    datafile_path = datafile_path.Append(FILE_PATH_LITERAL("third_party"))
263          .Append(FILE_PATH_LITERAL("safe_browsing"))
264          .Append(FILE_PATH_LITERAL("testing"))
265          .Append(kDataFile);
266    test_server_.reset(new LocalSafeBrowsingTestServer(datafile_path));
267    ASSERT_TRUE(test_server_->Start());
268    LOG(INFO) << "server is " << test_server_->host_port_pair().ToString();
269
270    // Point to the testing server for all SafeBrowsing requests.
271    std::string url_prefix = test_server_->GetURL("safebrowsing").spec();
272    sb_factory_.reset(new TestSafeBrowsingServiceFactory(url_prefix));
273    SafeBrowsingService::RegisterFactory(sb_factory_.get());
274
275    InProcessBrowserTest::SetUp();
276  }
277
278  virtual void TearDown() OVERRIDE {
279    InProcessBrowserTest::TearDown();
280
281    SafeBrowsingService::RegisterFactory(NULL);
282  }
283
284  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
285    // This test uses loopback. No need to use IPv6 especially it makes
286    // local requests slow on Windows trybot when ipv6 local address [::1]
287    // is not setup.
288    command_line->AppendSwitch(switches::kDisableIPv6);
289
290    // TODO(lzheng): The test server does not understand download related
291    // requests. We need to fix the server.
292    command_line->AppendSwitch(switches::kSbDisableDownloadProtection);
293
294    // TODO(gcasto): Generate new testing data that includes the
295    // client-side phishing whitelist.
296    command_line->AppendSwitch(
297        switches::kDisableClientSidePhishingDetection);
298
299    // TODO(kalman): Generate new testing data that includes the extension
300    // blacklist.
301    command_line->AppendSwitch(switches::kSbDisableExtensionBlacklist);
302
303    // TODO(tburkard): Generate new testing data that includes the side-effect
304    // free whitelist.
305    command_line->AppendSwitch(switches::kSbDisableSideEffectFreeWhitelist);
306  }
307
308  void SetTestStep(int step) {
309    std::string test_step = base::StringPrintf("test_step=%d", step);
310    safe_browsing_service_->protocol_manager_->set_additional_query(test_step);
311  }
312
313 private:
314  scoped_ptr<TestSafeBrowsingServiceFactory> sb_factory_;
315
316  SafeBrowsingService* safe_browsing_service_;
317
318  scoped_ptr<net::SpawnedTestServer> test_server_;
319
320  // Protects all variables below since they are read on UI thread
321  // but updated on IO thread or safebrowsing thread.
322  base::Lock update_status_mutex_;
323
324  // States associated with safebrowsing service updates.
325  bool is_database_ready_;
326  base::Time last_update_;
327  bool is_update_scheduled_;
328  // Indicates if there is a match between a URL's prefix and safebrowsing
329  // database (thus potentially it is a phishing URL).
330  bool is_checked_url_in_db_;
331  // True if last verified URL is not a phishing URL and thus it is safe.
332  bool is_checked_url_safe_;
333
334  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTest);
335};
336
337// A ref counted helper class that handles callbacks between IO thread and UI
338// thread.
339class SafeBrowsingServerTestHelper
340    : public base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>,
341      public SafeBrowsingDatabaseManager::Client,
342      public net::URLFetcherDelegate {
343 public:
344  SafeBrowsingServerTestHelper(SafeBrowsingServerTest* safe_browsing_test,
345                               net::URLRequestContextGetter* request_context)
346      : safe_browsing_test_(safe_browsing_test),
347        response_status_(net::URLRequestStatus::FAILED),
348        request_context_(request_context) {
349  }
350
351  // Callbacks for SafeBrowsingDatabaseManager::Client.
352  virtual void OnCheckBrowseUrlResult(const GURL& url,
353                                      SBThreatType threat_type,
354                                      const std::string& metadata) OVERRIDE {
355    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
356    EXPECT_TRUE(safe_browsing_test_->is_checked_url_in_db());
357    safe_browsing_test_->set_is_checked_url_safe(
358        threat_type == SB_THREAT_TYPE_SAFE);
359    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
360        base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone, this));
361  }
362
363  virtual void OnBlockingPageComplete(bool proceed) {
364    NOTREACHED() << "Not implemented.";
365  }
366
367  // Functions and callbacks related to CheckUrl. These are used to verify
368  // phishing URLs.
369  void CheckUrl(const GURL& url) {
370    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
371        base::Bind(&SafeBrowsingServerTestHelper::CheckUrlOnIOThread,
372                   this, url));
373    content::RunMessageLoop();
374  }
375  void CheckUrlOnIOThread(const GURL& url) {
376    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
377    safe_browsing_test_->CheckUrl(this, url);
378    if (!safe_browsing_test_->is_checked_url_in_db()) {
379      // Ends the checking since this URL's prefix is not in database.
380      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
381        base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone, this));
382    }
383    // Otherwise, OnCheckUrlDone is called in OnUrlCheckResult since
384    // safebrowsing service further fetches hashes from safebrowsing server.
385  }
386
387  void OnCheckUrlDone() {
388    StopUILoop();
389  }
390
391  // Updates status from IO Thread.
392  void CheckStatusOnIOThread() {
393    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
394    safe_browsing_test_->UpdateSafeBrowsingStatus();
395    safe_browsing_test_->SafeBrowsingMessageLoop()->PostTask(FROM_HERE,
396        base::Bind(&SafeBrowsingServerTestHelper::CheckIsDatabaseReady, this));
397  }
398
399  // Checks status in SafeBrowsing Thread.
400  void CheckIsDatabaseReady() {
401    EXPECT_EQ(base::MessageLoop::current(),
402              safe_browsing_test_->SafeBrowsingMessageLoop());
403    safe_browsing_test_->CheckIsDatabaseReady();
404    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
405        base::Bind(&SafeBrowsingServerTestHelper::OnWaitForStatusUpdateDone,
406                   this));
407  }
408
409  void OnWaitForStatusUpdateDone() {
410    StopUILoop();
411  }
412
413  // Update safebrowsing status.
414  void UpdateStatus() {
415    BrowserThread::PostTask(
416        BrowserThread::IO,
417        FROM_HERE,
418        base::Bind(&SafeBrowsingServerTestHelper::CheckStatusOnIOThread, this));
419    // Will continue after OnWaitForStatusUpdateDone().
420    content::RunMessageLoop();
421  }
422
423  // Calls test server to fetch database for verification.
424  net::URLRequestStatus::Status FetchDBToVerify(
425      const net::SpawnedTestServer& test_server,
426      int test_step) {
427    // TODO(lzheng): Remove chunk_type=add once it is not needed by the server.
428    std::string path = base::StringPrintf(
429        "%s?client=chromium&appver=1.0&pver=3.0&test_step=%d&chunk_type=add",
430        kDBVerifyPath, test_step);
431    return FetchUrl(test_server.GetURL(path));
432  }
433
434  // Calls test server to fetch URLs for verification.
435  net::URLRequestStatus::Status FetchUrlsToVerify(
436      const net::SpawnedTestServer& test_server,
437      int test_step) {
438    std::string path = base::StringPrintf(
439        "%s?client=chromium&appver=1.0&pver=3.0&test_step=%d",
440        kUrlVerifyPath, test_step);
441    return FetchUrl(test_server.GetURL(path));
442  }
443
444  // Calls test server to check if test data is done. E.g.: if there is a
445  // bad URL that server expects test to fetch full hash but the test didn't,
446  // this verification will fail.
447  net::URLRequestStatus::Status VerifyTestComplete(
448      const net::SpawnedTestServer& test_server,
449      int test_step) {
450    std::string path = base::StringPrintf(
451        "%s?test_step=%d", kTestCompletePath, test_step);
452    return FetchUrl(test_server.GetURL(path));
453  }
454
455  // Callback for URLFetcher.
456  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
457    source->GetResponseAsString(&response_data_);
458    response_status_ = source->GetStatus().status();
459    StopUILoop();
460  }
461
462  const std::string& response_data() {
463    return response_data_;
464  }
465
466 private:
467  friend class base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>;
468  virtual ~SafeBrowsingServerTestHelper() {}
469
470  // Stops UI loop after desired status is updated.
471  void StopUILoop() {
472    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
473    base::MessageLoopForUI::current()->Quit();
474  }
475
476  // Fetch a URL. If message_loop_started is true, starts the message loop
477  // so the caller could wait till OnURLFetchComplete is called.
478  net::URLRequestStatus::Status FetchUrl(const GURL& url) {
479    url_fetcher_.reset(net::URLFetcher::Create(
480        url, net::URLFetcher::GET, this));
481    url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
482    url_fetcher_->SetRequestContext(request_context_);
483    url_fetcher_->Start();
484    content::RunMessageLoop();
485    return response_status_;
486  }
487
488  base::OneShotTimer<SafeBrowsingServerTestHelper> check_update_timer_;
489  SafeBrowsingServerTest* safe_browsing_test_;
490  scoped_ptr<net::URLFetcher> url_fetcher_;
491  std::string response_data_;
492  net::URLRequestStatus::Status response_status_;
493  net::URLRequestContextGetter* request_context_;
494  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTestHelper);
495};
496
497// TODO(shess): Disabled pending new data for third_party/safe_browsing/testing/
498IN_PROC_BROWSER_TEST_F(SafeBrowsingServerTest,
499                       DISABLED_SafeBrowsingServerTest) {
500  LOG(INFO) << "Start test";
501  ASSERT_TRUE(InitSafeBrowsingService());
502
503  net::URLRequestContextGetter* request_context =
504      browser()->profile()->GetRequestContext();
505  scoped_refptr<SafeBrowsingServerTestHelper> safe_browsing_helper(
506      new SafeBrowsingServerTestHelper(this, request_context));
507  int last_step = 0;
508
509  // Waits and makes sure safebrowsing update is not happening.
510  // The wait will stop once OnWaitForStatusUpdateDone in
511  // safe_browsing_helper is called and status from safe_browsing_service_
512  // is checked.
513  safe_browsing_helper->UpdateStatus();
514  EXPECT_TRUE(is_database_ready());
515  EXPECT_FALSE(is_update_scheduled());
516  EXPECT_TRUE(last_update().is_null());
517  // Starts updates. After each update, the test will fetch a list of URLs with
518  // expected results to verify with safebrowsing service. If there is no error,
519  // the test moves on to the next step to get more update chunks.
520  // This repeats till there is no update data.
521  for (int step = 1;; step++) {
522    // Every step should be a fresh start.
523    SCOPED_TRACE(base::StringPrintf("step=%d", step));
524    EXPECT_TRUE(is_database_ready());
525    EXPECT_FALSE(is_update_scheduled());
526
527    // Starts safebrowsing update on IO thread. Waits till scheduled
528    // update finishes.
529    base::Time now = base::Time::Now();
530    SetTestStep(step);
531    ForceUpdate();
532
533    safe_browsing_helper->UpdateStatus();
534    EXPECT_TRUE(is_database_ready());
535    EXPECT_FALSE(is_update_scheduled());
536    EXPECT_FALSE(last_update().is_null());
537    if (last_update() < now) {
538      // This means no data available anymore.
539      break;
540    }
541
542    // Fetches URLs to verify and waits till server responses with data.
543    EXPECT_EQ(net::URLRequestStatus::SUCCESS,
544              safe_browsing_helper->FetchUrlsToVerify(test_server(), step));
545
546    std::vector<PhishingUrl> phishing_urls;
547    EXPECT_TRUE(ParsePhishingUrls(safe_browsing_helper->response_data(),
548                                  &phishing_urls));
549    EXPECT_GT(phishing_urls.size(), 0U);
550    for (size_t j = 0; j < phishing_urls.size(); ++j) {
551      // Verifes with server if a URL is a phishing URL and waits till server
552      // responses.
553      safe_browsing_helper->CheckUrl(GURL(phishing_urls[j].url));
554      if (phishing_urls[j].is_phishing) {
555        EXPECT_TRUE(is_checked_url_in_db())
556            << phishing_urls[j].url
557            << " is_phishing: " << phishing_urls[j].is_phishing
558            << " test step: " << step;
559        EXPECT_FALSE(is_checked_url_safe())
560            << phishing_urls[j].url
561            << " is_phishing: " << phishing_urls[j].is_phishing
562            << " test step: " << step;
563      } else {
564        EXPECT_TRUE(is_checked_url_safe())
565            << phishing_urls[j].url
566            << " is_phishing: " << phishing_urls[j].is_phishing
567            << " test step: " << step;
568      }
569    }
570    // TODO(lzheng): We should verify the fetched database with local
571    // database to make sure they match.
572    EXPECT_EQ(net::URLRequestStatus::SUCCESS,
573              safe_browsing_helper->FetchDBToVerify(test_server(), step));
574    EXPECT_GT(safe_browsing_helper->response_data().size(), 0U);
575    last_step = step;
576  }
577
578  // Verifies with server if test is done and waits till server responses.
579  EXPECT_EQ(net::URLRequestStatus::SUCCESS,
580            safe_browsing_helper->VerifyTestComplete(test_server(), last_step));
581  EXPECT_EQ("yes", safe_browsing_helper->response_data());
582}
583