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.
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
9 // http://code.google.com/p/google-safe-browsing/wiki/ProtocolTesting
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.
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"
55 using content::BrowserThread
;
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";
67 std::string list_name
;
71 // Parses server response for verify_urls. The expected format is:
73 // first.random.url.com/ internal-test-shavar yes
74 // second.random.url.com/ internal-test-shavar yes
76 bool ParsePhishingUrls(const std::string
& data
,
77 std::vector
<PhishingUrl
>* phishing_urls
) {
81 std::vector
<std::string
> urls
;
82 base::SplitString(data
, '\n', &urls
);
83 for (size_t i
= 0; i
< urls
.size(); ++i
) {
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: "
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;
102 LOG(ERROR
) << "Unrecognized expectation in " << urls
[i
]
103 << ": " << record_parts
[2];
106 phishing_urls
->push_back(phishing_url
);
111 class FakeSafeBrowsingService
: public SafeBrowsingService
{
113 explicit FakeSafeBrowsingService(const std::string
& url_prefix
)
114 : url_prefix_(url_prefix
) {}
116 virtual SafeBrowsingProtocolConfig
GetProtocolConfig() const OVERRIDE
{
117 SafeBrowsingProtocolConfig config
;
118 config
.url_prefix
= url_prefix_
;
119 // Makes sure the auto update is not triggered. The tests will force the
120 // update when needed.
121 config
.disable_auto_update
= true;
122 config
.client_name
= "browser_tests";
127 virtual ~FakeSafeBrowsingService() {}
129 std::string url_prefix_
;
131 DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService
);
134 // Factory that creates FakeSafeBrowsingService instances.
135 class TestSafeBrowsingServiceFactory
: public SafeBrowsingServiceFactory
{
137 explicit TestSafeBrowsingServiceFactory(const std::string
& url_prefix
)
138 : url_prefix_(url_prefix
) {}
140 virtual SafeBrowsingService
* CreateSafeBrowsingService() OVERRIDE
{
141 return new FakeSafeBrowsingService(url_prefix_
);
145 std::string url_prefix_
;
150 // This starts the browser and keeps status of states related to SafeBrowsing.
151 class SafeBrowsingServerTest
: public InProcessBrowserTest
{
153 SafeBrowsingServerTest()
154 : safe_browsing_service_(NULL
),
155 is_database_ready_(true),
156 is_update_scheduled_(false),
157 is_checked_url_in_db_(false),
158 is_checked_url_safe_(false) {
161 virtual ~SafeBrowsingServerTest() {
164 void UpdateSafeBrowsingStatus() {
165 ASSERT_TRUE(safe_browsing_service_
);
166 base::AutoLock
lock(update_status_mutex_
);
167 last_update_
= safe_browsing_service_
->protocol_manager_
->last_update();
168 is_update_scheduled_
=
169 safe_browsing_service_
->protocol_manager_
->update_timer_
.IsRunning();
173 content::WindowedNotificationObserver
observer(
174 chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE
,
175 content::Source
<SafeBrowsingDatabaseManager
>(database_manager()));
176 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
177 base::Bind(&SafeBrowsingServerTest::ForceUpdateOnIOThread
,
182 void ForceUpdateOnIOThread() {
183 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
184 ASSERT_TRUE(safe_browsing_service_
);
185 safe_browsing_service_
->protocol_manager_
->ForceScheduleNextUpdate(
186 base::TimeDelta::FromSeconds(0));
189 void CheckIsDatabaseReady() {
190 base::AutoLock
lock(update_status_mutex_
);
191 is_database_ready_
= !database_manager()->database_update_in_progress_
;
194 void CheckUrl(SafeBrowsingDatabaseManager::Client
* helper
, const GURL
& url
) {
195 ASSERT_TRUE(safe_browsing_service_
);
196 base::AutoLock
lock(update_status_mutex_
);
197 if (database_manager()->CheckBrowseUrl(url
, helper
)) {
198 is_checked_url_in_db_
= false;
199 is_checked_url_safe_
= true;
201 // In this case, Safebrowsing service will fetch the full hash
202 // from the server and examine that. Once it is done,
203 // set_is_checked_url_safe() will be called via callback.
204 is_checked_url_in_db_
= true;
208 SafeBrowsingDatabaseManager
* database_manager() {
209 return safe_browsing_service_
->database_manager().get();
212 bool is_checked_url_in_db() {
213 base::AutoLock
l(update_status_mutex_
);
214 return is_checked_url_in_db_
;
217 void set_is_checked_url_safe(bool safe
) {
218 base::AutoLock
l(update_status_mutex_
);
219 is_checked_url_safe_
= safe
;
222 bool is_checked_url_safe() {
223 base::AutoLock
l(update_status_mutex_
);
224 return is_checked_url_safe_
;
227 bool is_database_ready() {
228 base::AutoLock
l(update_status_mutex_
);
229 return is_database_ready_
;
232 base::Time
last_update() {
233 base::AutoLock
l(update_status_mutex_
);
237 bool is_update_scheduled() {
238 base::AutoLock
l(update_status_mutex_
);
239 return is_update_scheduled_
;
242 base::MessageLoop
* SafeBrowsingMessageLoop() {
243 return database_manager()->safe_browsing_thread_
->message_loop();
246 const net::SpawnedTestServer
& test_server() const {
247 return *test_server_
;
251 bool InitSafeBrowsingService() {
252 safe_browsing_service_
= g_browser_process
->safe_browsing_service();
253 return safe_browsing_service_
!= NULL
;
256 virtual void SetUp() OVERRIDE
{
257 base::FilePath datafile_path
;
258 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT
, &datafile_path
));
260 datafile_path
= datafile_path
.Append(FILE_PATH_LITERAL("third_party"))
261 .Append(FILE_PATH_LITERAL("safe_browsing"))
262 .Append(FILE_PATH_LITERAL("testing"))
264 test_server_
.reset(new LocalSafeBrowsingTestServer(datafile_path
));
265 ASSERT_TRUE(test_server_
->Start());
266 LOG(INFO
) << "server is " << test_server_
->host_port_pair().ToString();
268 // Point to the testing server for all SafeBrowsing requests.
269 std::string url_prefix
= test_server_
->GetURL("safebrowsing").spec();
270 sb_factory_
.reset(new TestSafeBrowsingServiceFactory(url_prefix
));
271 SafeBrowsingService::RegisterFactory(sb_factory_
.get());
273 InProcessBrowserTest::SetUp();
276 virtual void TearDown() OVERRIDE
{
277 InProcessBrowserTest::TearDown();
279 SafeBrowsingService::RegisterFactory(NULL
);
282 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
283 // This test uses loopback. No need to use IPv6 especially it makes
284 // local requests slow on Windows trybot when ipv6 local address [::1]
286 command_line
->AppendSwitch(switches::kDisableIPv6
);
288 // TODO(lzheng): The test server does not understand download related
289 // requests. We need to fix the server.
290 command_line
->AppendSwitch(switches::kSbDisableDownloadProtection
);
292 // TODO(gcasto): Generate new testing data that includes the
293 // client-side phishing whitelist.
294 command_line
->AppendSwitch(
295 switches::kDisableClientSidePhishingDetection
);
297 // TODO(kalman): Generate new testing data that includes the extension
299 command_line
->AppendSwitch(switches::kSbDisableExtensionBlacklist
);
301 // TODO(tburkard): Generate new testing data that includes the side-effect
303 command_line
->AppendSwitch(switches::kSbDisableSideEffectFreeWhitelist
);
306 void SetTestStep(int step
) {
307 std::string test_step
= base::StringPrintf("test_step=%d", step
);
308 safe_browsing_service_
->protocol_manager_
->set_additional_query(test_step
);
312 scoped_ptr
<TestSafeBrowsingServiceFactory
> sb_factory_
;
314 SafeBrowsingService
* safe_browsing_service_
;
316 scoped_ptr
<net::SpawnedTestServer
> test_server_
;
318 // Protects all variables below since they are read on UI thread
319 // but updated on IO thread or safebrowsing thread.
320 base::Lock update_status_mutex_
;
322 // States associated with safebrowsing service updates.
323 bool is_database_ready_
;
324 base::Time last_update_
;
325 bool is_update_scheduled_
;
326 // Indicates if there is a match between a URL's prefix and safebrowsing
327 // database (thus potentially it is a phishing URL).
328 bool is_checked_url_in_db_
;
329 // True if last verified URL is not a phishing URL and thus it is safe.
330 bool is_checked_url_safe_
;
332 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTest
);
335 // A ref counted helper class that handles callbacks between IO thread and UI
337 class SafeBrowsingServerTestHelper
338 : public base::RefCountedThreadSafe
<SafeBrowsingServerTestHelper
>,
339 public SafeBrowsingDatabaseManager::Client
,
340 public net::URLFetcherDelegate
{
342 SafeBrowsingServerTestHelper(SafeBrowsingServerTest
* safe_browsing_test
,
343 net::URLRequestContextGetter
* request_context
)
344 : safe_browsing_test_(safe_browsing_test
),
345 response_status_(net::URLRequestStatus::FAILED
),
346 request_context_(request_context
) {
349 // Callbacks for SafeBrowsingDatabaseManager::Client.
350 virtual void OnCheckBrowseUrlResult(const GURL
& url
,
351 SBThreatType threat_type
) OVERRIDE
{
352 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
353 EXPECT_TRUE(safe_browsing_test_
->is_checked_url_in_db());
354 safe_browsing_test_
->set_is_checked_url_safe(
355 threat_type
== SB_THREAT_TYPE_SAFE
);
356 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
357 base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone
, this));
360 virtual void OnBlockingPageComplete(bool proceed
) {
361 NOTREACHED() << "Not implemented.";
364 // Functions and callbacks related to CheckUrl. These are used to verify
366 void CheckUrl(const GURL
& url
) {
367 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
368 base::Bind(&SafeBrowsingServerTestHelper::CheckUrlOnIOThread
,
370 content::RunMessageLoop();
372 void CheckUrlOnIOThread(const GURL
& url
) {
373 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
374 safe_browsing_test_
->CheckUrl(this, url
);
375 if (!safe_browsing_test_
->is_checked_url_in_db()) {
376 // Ends the checking since this URL's prefix is not in database.
377 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
378 base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone
, this));
380 // Otherwise, OnCheckUrlDone is called in OnUrlCheckResult since
381 // safebrowsing service further fetches hashes from safebrowsing server.
384 void OnCheckUrlDone() {
388 // Updates status from IO Thread.
389 void CheckStatusOnIOThread() {
390 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
391 safe_browsing_test_
->UpdateSafeBrowsingStatus();
392 safe_browsing_test_
->SafeBrowsingMessageLoop()->PostTask(FROM_HERE
,
393 base::Bind(&SafeBrowsingServerTestHelper::CheckIsDatabaseReady
, this));
396 // Checks status in SafeBrowsing Thread.
397 void CheckIsDatabaseReady() {
398 EXPECT_EQ(base::MessageLoop::current(),
399 safe_browsing_test_
->SafeBrowsingMessageLoop());
400 safe_browsing_test_
->CheckIsDatabaseReady();
401 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
402 base::Bind(&SafeBrowsingServerTestHelper::OnWaitForStatusUpdateDone
,
406 void OnWaitForStatusUpdateDone() {
410 // Update safebrowsing status.
411 void UpdateStatus() {
412 BrowserThread::PostTask(
415 base::Bind(&SafeBrowsingServerTestHelper::CheckStatusOnIOThread
, this));
416 // Will continue after OnWaitForStatusUpdateDone().
417 content::RunMessageLoop();
420 // Calls test server to fetch database for verification.
421 net::URLRequestStatus::Status
FetchDBToVerify(
422 const net::SpawnedTestServer
& test_server
,
424 // TODO(lzheng): Remove chunk_type=add once it is not needed by the server.
425 std::string path
= base::StringPrintf(
426 "%s?client=chromium&appver=1.0&pver=2.2&test_step=%d&chunk_type=add",
427 kDBVerifyPath
, test_step
);
428 return FetchUrl(test_server
.GetURL(path
));
431 // Calls test server to fetch URLs for verification.
432 net::URLRequestStatus::Status
FetchUrlsToVerify(
433 const net::SpawnedTestServer
& test_server
,
435 std::string path
= base::StringPrintf(
436 "%s?client=chromium&appver=1.0&pver=2.2&test_step=%d",
437 kUrlVerifyPath
, test_step
);
438 return FetchUrl(test_server
.GetURL(path
));
441 // Calls test server to check if test data is done. E.g.: if there is a
442 // bad URL that server expects test to fetch full hash but the test didn't,
443 // this verification will fail.
444 net::URLRequestStatus::Status
VerifyTestComplete(
445 const net::SpawnedTestServer
& test_server
,
447 std::string path
= base::StringPrintf(
448 "%s?test_step=%d", kTestCompletePath
, test_step
);
449 return FetchUrl(test_server
.GetURL(path
));
452 // Callback for URLFetcher.
453 virtual void OnURLFetchComplete(const net::URLFetcher
* source
) OVERRIDE
{
454 source
->GetResponseAsString(&response_data_
);
455 response_status_
= source
->GetStatus().status();
459 const std::string
& response_data() {
460 return response_data_
;
464 friend class base::RefCountedThreadSafe
<SafeBrowsingServerTestHelper
>;
465 virtual ~SafeBrowsingServerTestHelper() {}
467 // Stops UI loop after desired status is updated.
469 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI
));
470 base::MessageLoopForUI::current()->Quit();
473 // Fetch a URL. If message_loop_started is true, starts the message loop
474 // so the caller could wait till OnURLFetchComplete is called.
475 net::URLRequestStatus::Status
FetchUrl(const GURL
& url
) {
476 url_fetcher_
.reset(net::URLFetcher::Create(
477 url
, net::URLFetcher::GET
, this));
478 url_fetcher_
->SetLoadFlags(net::LOAD_DISABLE_CACHE
);
479 url_fetcher_
->SetRequestContext(request_context_
);
480 url_fetcher_
->Start();
481 content::RunMessageLoop();
482 return response_status_
;
485 base::OneShotTimer
<SafeBrowsingServerTestHelper
> check_update_timer_
;
486 SafeBrowsingServerTest
* safe_browsing_test_
;
487 scoped_ptr
<net::URLFetcher
> url_fetcher_
;
488 std::string response_data_
;
489 net::URLRequestStatus::Status response_status_
;
490 net::URLRequestContextGetter
* request_context_
;
491 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTestHelper
);
494 IN_PROC_BROWSER_TEST_F(SafeBrowsingServerTest
,
495 SafeBrowsingServerTest
) {
496 LOG(INFO
) << "Start test";
497 ASSERT_TRUE(InitSafeBrowsingService());
499 net::URLRequestContextGetter
* request_context
=
500 browser()->profile()->GetRequestContext();
501 scoped_refptr
<SafeBrowsingServerTestHelper
> safe_browsing_helper(
502 new SafeBrowsingServerTestHelper(this, request_context
));
505 // Waits and makes sure safebrowsing update is not happening.
506 // The wait will stop once OnWaitForStatusUpdateDone in
507 // safe_browsing_helper is called and status from safe_browsing_service_
509 safe_browsing_helper
->UpdateStatus();
510 EXPECT_TRUE(is_database_ready());
511 EXPECT_FALSE(is_update_scheduled());
512 EXPECT_TRUE(last_update().is_null());
513 // Starts updates. After each update, the test will fetch a list of URLs with
514 // expected results to verify with safebrowsing service. If there is no error,
515 // the test moves on to the next step to get more update chunks.
516 // This repeats till there is no update data.
517 for (int step
= 1;; step
++) {
518 // Every step should be a fresh start.
519 SCOPED_TRACE(base::StringPrintf("step=%d", step
));
520 EXPECT_TRUE(is_database_ready());
521 EXPECT_FALSE(is_update_scheduled());
523 // Starts safebrowsing update on IO thread. Waits till scheduled
525 base::Time now
= base::Time::Now();
529 safe_browsing_helper
->UpdateStatus();
530 EXPECT_TRUE(is_database_ready());
531 EXPECT_FALSE(is_update_scheduled());
532 EXPECT_FALSE(last_update().is_null());
533 if (last_update() < now
) {
534 // This means no data available anymore.
538 // Fetches URLs to verify and waits till server responses with data.
539 EXPECT_EQ(net::URLRequestStatus::SUCCESS
,
540 safe_browsing_helper
->FetchUrlsToVerify(test_server(), step
));
542 std::vector
<PhishingUrl
> phishing_urls
;
543 EXPECT_TRUE(ParsePhishingUrls(safe_browsing_helper
->response_data(),
545 EXPECT_GT(phishing_urls
.size(), 0U);
546 for (size_t j
= 0; j
< phishing_urls
.size(); ++j
) {
547 // Verifes with server if a URL is a phishing URL and waits till server
549 safe_browsing_helper
->CheckUrl(GURL(phishing_urls
[j
].url
));
550 if (phishing_urls
[j
].is_phishing
) {
551 EXPECT_TRUE(is_checked_url_in_db())
552 << phishing_urls
[j
].url
553 << " is_phishing: " << phishing_urls
[j
].is_phishing
554 << " test step: " << step
;
555 EXPECT_FALSE(is_checked_url_safe())
556 << phishing_urls
[j
].url
557 << " is_phishing: " << phishing_urls
[j
].is_phishing
558 << " test step: " << step
;
560 EXPECT_TRUE(is_checked_url_safe())
561 << phishing_urls
[j
].url
562 << " is_phishing: " << phishing_urls
[j
].is_phishing
563 << " test step: " << step
;
566 // TODO(lzheng): We should verify the fetched database with local
567 // database to make sure they match.
568 EXPECT_EQ(net::URLRequestStatus::SUCCESS
,
569 safe_browsing_helper
->FetchDBToVerify(test_server(), step
));
570 EXPECT_GT(safe_browsing_helper
->response_data().size(), 0U);
574 // Verifies with server if test is done and waits till server responses.
575 EXPECT_EQ(net::URLRequestStatus::SUCCESS
,
576 safe_browsing_helper
->VerifyTestComplete(test_server(), last_step
));
577 EXPECT_EQ("yes", safe_browsing_helper
->response_data());