Home | History | Annotate | Download | only in safe_browsing
      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 
     55 using content::BrowserThread;
     56 
     57 namespace {
     58 
     59 const base::FilePath::CharType kDataFile[] =
     60     FILE_PATH_LITERAL("testing_input_nomac.dat");
     61 const char kUrlVerifyPath[] = "safebrowsing/verify_urls";
     62 const char kDBVerifyPath[] = "safebrowsing/verify_database";
     63 const char kTestCompletePath[] = "test_complete";
     64 
     65 struct 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 // ...
     76 bool 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(content::kHttpScheme) +
     95         "://" + record_parts[0];
     96     phishing_url.list_name = record_parts[1];
     97     if (record_parts[2] == "yes") {
     98       phishing_url.is_phishing = true;
     99     } else if (record_parts[2] == "no") {
    100       phishing_url.is_phishing = false;
    101     } else {
    102       LOG(ERROR) << "Unrecognized expectation in " << urls[i]
    103                  << ": " << record_parts[2];
    104       return false;
    105     }
    106     phishing_urls->push_back(phishing_url);
    107   }
    108   return true;
    109 }
    110 
    111 }  // namespace
    112 
    113 // This starts the browser and keeps status of states related to SafeBrowsing.
    114 class SafeBrowsingServerTest : public InProcessBrowserTest {
    115  public:
    116   SafeBrowsingServerTest()
    117     : safe_browsing_service_(NULL),
    118       is_database_ready_(true),
    119       is_update_scheduled_(false),
    120       is_checked_url_in_db_(false),
    121       is_checked_url_safe_(false) {
    122   }
    123 
    124   virtual ~SafeBrowsingServerTest() {
    125   }
    126 
    127   void UpdateSafeBrowsingStatus() {
    128     ASSERT_TRUE(safe_browsing_service_);
    129     base::AutoLock lock(update_status_mutex_);
    130     last_update_ = safe_browsing_service_->protocol_manager_->last_update();
    131     is_update_scheduled_ =
    132         safe_browsing_service_->protocol_manager_->update_timer_.IsRunning();
    133   }
    134 
    135   void ForceUpdate() {
    136     content::WindowedNotificationObserver observer(
    137         chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
    138         content::Source<SafeBrowsingDatabaseManager>(database_manager()));
    139     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    140         base::Bind(&SafeBrowsingServerTest::ForceUpdateOnIOThread,
    141                    this));
    142     observer.Wait();
    143   }
    144 
    145   void ForceUpdateOnIOThread() {
    146     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    147     ASSERT_TRUE(safe_browsing_service_);
    148     safe_browsing_service_->protocol_manager_->ForceScheduleNextUpdate(
    149         base::TimeDelta::FromSeconds(0));
    150   }
    151 
    152   void CheckIsDatabaseReady() {
    153     base::AutoLock lock(update_status_mutex_);
    154     is_database_ready_ = !database_manager()->database_update_in_progress_;
    155   }
    156 
    157   void CheckUrl(SafeBrowsingDatabaseManager::Client* helper, const GURL& url) {
    158     ASSERT_TRUE(safe_browsing_service_);
    159     base::AutoLock lock(update_status_mutex_);
    160     if (database_manager()->CheckBrowseUrl(url, helper)) {
    161       is_checked_url_in_db_ = false;
    162       is_checked_url_safe_ = true;
    163     } else {
    164       // In this case, Safebrowsing service will fetch the full hash
    165       // from the server and examine that. Once it is done,
    166       // set_is_checked_url_safe() will be called via callback.
    167       is_checked_url_in_db_ = true;
    168     }
    169   }
    170 
    171   SafeBrowsingDatabaseManager* database_manager() {
    172     return safe_browsing_service_->database_manager().get();
    173   }
    174 
    175   bool is_checked_url_in_db() {
    176     base::AutoLock l(update_status_mutex_);
    177     return is_checked_url_in_db_;
    178   }
    179 
    180   void set_is_checked_url_safe(bool safe) {
    181     base::AutoLock l(update_status_mutex_);
    182     is_checked_url_safe_ = safe;
    183   }
    184 
    185   bool is_checked_url_safe() {
    186     base::AutoLock l(update_status_mutex_);
    187     return is_checked_url_safe_;
    188   }
    189 
    190   bool is_database_ready() {
    191     base::AutoLock l(update_status_mutex_);
    192     return is_database_ready_;
    193   }
    194 
    195   base::Time last_update() {
    196     base::AutoLock l(update_status_mutex_);
    197     return last_update_;
    198   }
    199 
    200   bool is_update_scheduled() {
    201     base::AutoLock l(update_status_mutex_);
    202     return is_update_scheduled_;
    203   }
    204 
    205   base::MessageLoop* SafeBrowsingMessageLoop() {
    206     return database_manager()->safe_browsing_thread_->message_loop();
    207   }
    208 
    209   const net::SpawnedTestServer& test_server() const {
    210     return *test_server_;
    211   }
    212 
    213  protected:
    214   bool InitSafeBrowsingService() {
    215     safe_browsing_service_ = g_browser_process->safe_browsing_service();
    216     return safe_browsing_service_ != NULL;
    217   }
    218 
    219   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    220     base::FilePath datafile_path;
    221     ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &datafile_path));
    222 
    223     datafile_path = datafile_path.Append(FILE_PATH_LITERAL("third_party"))
    224           .Append(FILE_PATH_LITERAL("safe_browsing"))
    225           .Append(FILE_PATH_LITERAL("testing"))
    226           .Append(kDataFile);
    227     test_server_.reset(new LocalSafeBrowsingTestServer(datafile_path));
    228     ASSERT_TRUE(test_server_->Start());
    229     LOG(INFO) << "server is " << test_server_->host_port_pair().ToString();
    230 
    231     // Makes sure the auto update is not triggered. This test will force the
    232     // update when needed.
    233     command_line->AppendSwitch(switches::kSbDisableAutoUpdate);
    234 
    235     // This test uses loopback. No need to use IPv6 especially it makes
    236     // local requests slow on Windows trybot when ipv6 local address [::1]
    237     // is not setup.
    238     command_line->AppendSwitch(switches::kDisableIPv6);
    239 
    240     // TODO(lzheng): The test server does not understand download related
    241     // requests. We need to fix the server.
    242     command_line->AppendSwitch(switches::kSbDisableDownloadProtection);
    243 
    244     // TODO(gcasto): Generate new testing data that includes the
    245     // client-side phishing whitelist.
    246     command_line->AppendSwitch(
    247         switches::kDisableClientSidePhishingDetection);
    248 
    249     // TODO(kalman): Generate new testing data that includes the extension
    250     // blacklist.
    251     command_line->AppendSwitch(switches::kSbDisableExtensionBlacklist);
    252 
    253     // TODO(tburkard): Generate new testing data that includes the side-effect
    254     // free whitelist.
    255     command_line->AppendSwitch(switches::kSbDisableSideEffectFreeWhitelist);
    256 
    257     // Point to the testing server for all SafeBrowsing requests.
    258     std::string url_prefix = test_server_->GetURL("safebrowsing").spec();
    259     command_line->AppendSwitchASCII(switches::kSbURLPrefix, url_prefix);
    260   }
    261 
    262   void SetTestStep(int step) {
    263     std::string test_step = base::StringPrintf("test_step=%d", step);
    264     safe_browsing_service_->protocol_manager_->set_additional_query(test_step);
    265   }
    266 
    267  private:
    268   SafeBrowsingService* safe_browsing_service_;
    269 
    270   scoped_ptr<net::SpawnedTestServer> test_server_;
    271 
    272   // Protects all variables below since they are read on UI thread
    273   // but updated on IO thread or safebrowsing thread.
    274   base::Lock update_status_mutex_;
    275 
    276   // States associated with safebrowsing service updates.
    277   bool is_database_ready_;
    278   base::Time last_update_;
    279   bool is_update_scheduled_;
    280   // Indicates if there is a match between a URL's prefix and safebrowsing
    281   // database (thus potentially it is a phishing URL).
    282   bool is_checked_url_in_db_;
    283   // True if last verified URL is not a phishing URL and thus it is safe.
    284   bool is_checked_url_safe_;
    285 
    286   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTest);
    287 };
    288 
    289 // A ref counted helper class that handles callbacks between IO thread and UI
    290 // thread.
    291 class SafeBrowsingServerTestHelper
    292     : public base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>,
    293       public SafeBrowsingDatabaseManager::Client,
    294       public net::URLFetcherDelegate {
    295  public:
    296   SafeBrowsingServerTestHelper(SafeBrowsingServerTest* safe_browsing_test,
    297                                net::URLRequestContextGetter* request_context)
    298       : safe_browsing_test_(safe_browsing_test),
    299         response_status_(net::URLRequestStatus::FAILED),
    300         request_context_(request_context) {
    301   }
    302 
    303   // Callbacks for SafeBrowsingDatabaseManager::Client.
    304   virtual void OnCheckBrowseUrlResult(const GURL& url,
    305                                       SBThreatType threat_type) OVERRIDE {
    306     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    307     EXPECT_TRUE(safe_browsing_test_->is_checked_url_in_db());
    308     safe_browsing_test_->set_is_checked_url_safe(
    309         threat_type == SB_THREAT_TYPE_SAFE);
    310     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    311         base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone, this));
    312   }
    313 
    314   virtual void OnBlockingPageComplete(bool proceed) {
    315     NOTREACHED() << "Not implemented.";
    316   }
    317 
    318   // Functions and callbacks related to CheckUrl. These are used to verify
    319   // phishing URLs.
    320   void CheckUrl(const GURL& url) {
    321     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    322         base::Bind(&SafeBrowsingServerTestHelper::CheckUrlOnIOThread,
    323                    this, url));
    324     content::RunMessageLoop();
    325   }
    326   void CheckUrlOnIOThread(const GURL& url) {
    327     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    328     safe_browsing_test_->CheckUrl(this, url);
    329     if (!safe_browsing_test_->is_checked_url_in_db()) {
    330       // Ends the checking since this URL's prefix is not in database.
    331       BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    332         base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone, this));
    333     }
    334     // Otherwise, OnCheckUrlDone is called in OnUrlCheckResult since
    335     // safebrowsing service further fetches hashes from safebrowsing server.
    336   }
    337 
    338   void OnCheckUrlDone() {
    339     StopUILoop();
    340   }
    341 
    342   // Updates status from IO Thread.
    343   void CheckStatusOnIOThread() {
    344     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
    345     safe_browsing_test_->UpdateSafeBrowsingStatus();
    346     safe_browsing_test_->SafeBrowsingMessageLoop()->PostTask(FROM_HERE,
    347         base::Bind(&SafeBrowsingServerTestHelper::CheckIsDatabaseReady, this));
    348   }
    349 
    350   // Checks status in SafeBrowsing Thread.
    351   void CheckIsDatabaseReady() {
    352     EXPECT_EQ(base::MessageLoop::current(),
    353               safe_browsing_test_->SafeBrowsingMessageLoop());
    354     safe_browsing_test_->CheckIsDatabaseReady();
    355     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    356         base::Bind(&SafeBrowsingServerTestHelper::OnWaitForStatusUpdateDone,
    357                    this));
    358   }
    359 
    360   void OnWaitForStatusUpdateDone() {
    361     StopUILoop();
    362   }
    363 
    364   // Update safebrowsing status.
    365   void UpdateStatus() {
    366     BrowserThread::PostTask(
    367         BrowserThread::IO,
    368         FROM_HERE,
    369         base::Bind(&SafeBrowsingServerTestHelper::CheckStatusOnIOThread, this));
    370     // Will continue after OnWaitForStatusUpdateDone().
    371     content::RunMessageLoop();
    372   }
    373 
    374   // Calls test server to fetch database for verification.
    375   net::URLRequestStatus::Status FetchDBToVerify(
    376       const net::SpawnedTestServer& test_server,
    377       int test_step) {
    378     // TODO(lzheng): Remove chunk_type=add once it is not needed by the server.
    379     std::string path = base::StringPrintf(
    380         "%s?client=chromium&appver=1.0&pver=2.2&test_step=%d&chunk_type=add",
    381         kDBVerifyPath, test_step);
    382     return FetchUrl(test_server.GetURL(path));
    383   }
    384 
    385   // Calls test server to fetch URLs for verification.
    386   net::URLRequestStatus::Status FetchUrlsToVerify(
    387       const net::SpawnedTestServer& test_server,
    388       int test_step) {
    389     std::string path = base::StringPrintf(
    390         "%s?client=chromium&appver=1.0&pver=2.2&test_step=%d",
    391         kUrlVerifyPath, test_step);
    392     return FetchUrl(test_server.GetURL(path));
    393   }
    394 
    395   // Calls test server to check if test data is done. E.g.: if there is a
    396   // bad URL that server expects test to fetch full hash but the test didn't,
    397   // this verification will fail.
    398   net::URLRequestStatus::Status VerifyTestComplete(
    399       const net::SpawnedTestServer& test_server,
    400       int test_step) {
    401     std::string path = base::StringPrintf(
    402         "%s?test_step=%d", kTestCompletePath, test_step);
    403     return FetchUrl(test_server.GetURL(path));
    404   }
    405 
    406   // Callback for URLFetcher.
    407   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
    408     source->GetResponseAsString(&response_data_);
    409     response_status_ = source->GetStatus().status();
    410     StopUILoop();
    411   }
    412 
    413   const std::string& response_data() {
    414     return response_data_;
    415   }
    416 
    417  private:
    418   friend class base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>;
    419   virtual ~SafeBrowsingServerTestHelper() {}
    420 
    421   // Stops UI loop after desired status is updated.
    422   void StopUILoop() {
    423     EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
    424     base::MessageLoopForUI::current()->Quit();
    425   }
    426 
    427   // Fetch a URL. If message_loop_started is true, starts the message loop
    428   // so the caller could wait till OnURLFetchComplete is called.
    429   net::URLRequestStatus::Status FetchUrl(const GURL& url) {
    430     url_fetcher_.reset(net::URLFetcher::Create(
    431         url, net::URLFetcher::GET, this));
    432     url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    433     url_fetcher_->SetRequestContext(request_context_);
    434     url_fetcher_->Start();
    435     content::RunMessageLoop();
    436     return response_status_;
    437   }
    438 
    439   base::OneShotTimer<SafeBrowsingServerTestHelper> check_update_timer_;
    440   SafeBrowsingServerTest* safe_browsing_test_;
    441   scoped_ptr<net::URLFetcher> url_fetcher_;
    442   std::string response_data_;
    443   net::URLRequestStatus::Status response_status_;
    444   net::URLRequestContextGetter* request_context_;
    445   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTestHelper);
    446 };
    447 
    448 IN_PROC_BROWSER_TEST_F(SafeBrowsingServerTest,
    449                        SafeBrowsingServerTest) {
    450   LOG(INFO) << "Start test";
    451   ASSERT_TRUE(InitSafeBrowsingService());
    452 
    453   net::URLRequestContextGetter* request_context =
    454       browser()->profile()->GetRequestContext();
    455   scoped_refptr<SafeBrowsingServerTestHelper> safe_browsing_helper(
    456       new SafeBrowsingServerTestHelper(this, request_context));
    457   int last_step = 0;
    458 
    459   // Waits and makes sure safebrowsing update is not happening.
    460   // The wait will stop once OnWaitForStatusUpdateDone in
    461   // safe_browsing_helper is called and status from safe_browsing_service_
    462   // is checked.
    463   safe_browsing_helper->UpdateStatus();
    464   EXPECT_TRUE(is_database_ready());
    465   EXPECT_FALSE(is_update_scheduled());
    466   EXPECT_TRUE(last_update().is_null());
    467   // Starts updates. After each update, the test will fetch a list of URLs with
    468   // expected results to verify with safebrowsing service. If there is no error,
    469   // the test moves on to the next step to get more update chunks.
    470   // This repeats till there is no update data.
    471   for (int step = 1;; step++) {
    472     // Every step should be a fresh start.
    473     SCOPED_TRACE(base::StringPrintf("step=%d", step));
    474     EXPECT_TRUE(is_database_ready());
    475     EXPECT_FALSE(is_update_scheduled());
    476 
    477     // Starts safebrowsing update on IO thread. Waits till scheduled
    478     // update finishes.
    479     base::Time now = base::Time::Now();
    480     SetTestStep(step);
    481     ForceUpdate();
    482 
    483     safe_browsing_helper->UpdateStatus();
    484     EXPECT_TRUE(is_database_ready());
    485     EXPECT_FALSE(is_update_scheduled());
    486     EXPECT_FALSE(last_update().is_null());
    487     if (last_update() < now) {
    488       // This means no data available anymore.
    489       break;
    490     }
    491 
    492     // Fetches URLs to verify and waits till server responses with data.
    493     EXPECT_EQ(net::URLRequestStatus::SUCCESS,
    494               safe_browsing_helper->FetchUrlsToVerify(test_server(), step));
    495 
    496     std::vector<PhishingUrl> phishing_urls;
    497     EXPECT_TRUE(ParsePhishingUrls(safe_browsing_helper->response_data(),
    498                                   &phishing_urls));
    499     EXPECT_GT(phishing_urls.size(), 0U);
    500     for (size_t j = 0; j < phishing_urls.size(); ++j) {
    501       // Verifes with server if a URL is a phishing URL and waits till server
    502       // responses.
    503       safe_browsing_helper->CheckUrl(GURL(phishing_urls[j].url));
    504       if (phishing_urls[j].is_phishing) {
    505         EXPECT_TRUE(is_checked_url_in_db())
    506             << phishing_urls[j].url
    507             << " is_phishing: " << phishing_urls[j].is_phishing
    508             << " test step: " << step;
    509         EXPECT_FALSE(is_checked_url_safe())
    510             << phishing_urls[j].url
    511             << " is_phishing: " << phishing_urls[j].is_phishing
    512             << " test step: " << step;
    513       } else {
    514         EXPECT_TRUE(is_checked_url_safe())
    515             << phishing_urls[j].url
    516             << " is_phishing: " << phishing_urls[j].is_phishing
    517             << " test step: " << step;
    518       }
    519     }
    520     // TODO(lzheng): We should verify the fetched database with local
    521     // database to make sure they match.
    522     EXPECT_EQ(net::URLRequestStatus::SUCCESS,
    523               safe_browsing_helper->FetchDBToVerify(test_server(), step));
    524     EXPECT_GT(safe_browsing_helper->response_data().size(), 0U);
    525     last_step = step;
    526   }
    527 
    528   // Verifies with server if test is done and waits till server responses.
    529   EXPECT_EQ(net::URLRequestStatus::SUCCESS,
    530             safe_browsing_helper->VerifyTestComplete(test_server(), last_step));
    531   EXPECT_EQ("yes", safe_browsing_helper->response_data());
    532 }
    533