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 // ...
ParsePhishingUrls(const std::string & data,std::vector<PhishingUrl> * phishing_urls)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:
SafeBrowsingServerTest()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
~SafeBrowsingServerTest()124 virtual ~SafeBrowsingServerTest() {
125 }
126
UpdateSafeBrowsingStatus()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
ForceUpdate()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
ForceUpdateOnIOThread()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
CheckIsDatabaseReady()152 void CheckIsDatabaseReady() {
153 base::AutoLock lock(update_status_mutex_);
154 is_database_ready_ = !database_manager()->database_update_in_progress_;
155 }
156
CheckUrl(SafeBrowsingDatabaseManager::Client * helper,const GURL & url)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
database_manager()171 SafeBrowsingDatabaseManager* database_manager() {
172 return safe_browsing_service_->database_manager().get();
173 }
174
is_checked_url_in_db()175 bool is_checked_url_in_db() {
176 base::AutoLock l(update_status_mutex_);
177 return is_checked_url_in_db_;
178 }
179
set_is_checked_url_safe(bool safe)180 void set_is_checked_url_safe(bool safe) {
181 base::AutoLock l(update_status_mutex_);
182 is_checked_url_safe_ = safe;
183 }
184
is_checked_url_safe()185 bool is_checked_url_safe() {
186 base::AutoLock l(update_status_mutex_);
187 return is_checked_url_safe_;
188 }
189
is_database_ready()190 bool is_database_ready() {
191 base::AutoLock l(update_status_mutex_);
192 return is_database_ready_;
193 }
194
last_update()195 base::Time last_update() {
196 base::AutoLock l(update_status_mutex_);
197 return last_update_;
198 }
199
is_update_scheduled()200 bool is_update_scheduled() {
201 base::AutoLock l(update_status_mutex_);
202 return is_update_scheduled_;
203 }
204
SafeBrowsingMessageLoop()205 base::MessageLoop* SafeBrowsingMessageLoop() {
206 return database_manager()->safe_browsing_thread_->message_loop();
207 }
208
test_server() const209 const net::SpawnedTestServer& test_server() const {
210 return *test_server_;
211 }
212
213 protected:
InitSafeBrowsingService()214 bool InitSafeBrowsingService() {
215 safe_browsing_service_ = g_browser_process->safe_browsing_service();
216 return safe_browsing_service_ != NULL;
217 }
218
SetUpCommandLine(CommandLine * command_line)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
SetTestStep(int step)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:
SafeBrowsingServerTestHelper(SafeBrowsingServerTest * safe_browsing_test,net::URLRequestContextGetter * request_context)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.
OnCheckBrowseUrlResult(const GURL & url,SBThreatType threat_type)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
OnBlockingPageComplete(bool proceed)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.
CheckUrl(const GURL & url)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 }
CheckUrlOnIOThread(const GURL & url)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
OnCheckUrlDone()338 void OnCheckUrlDone() {
339 StopUILoop();
340 }
341
342 // Updates status from IO Thread.
CheckStatusOnIOThread()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.
CheckIsDatabaseReady()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
OnWaitForStatusUpdateDone()360 void OnWaitForStatusUpdateDone() {
361 StopUILoop();
362 }
363
364 // Update safebrowsing status.
UpdateStatus()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.
FetchDBToVerify(const net::SpawnedTestServer & test_server,int test_step)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.
FetchUrlsToVerify(const net::SpawnedTestServer & test_server,int test_step)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.
VerifyTestComplete(const net::SpawnedTestServer & test_server,int test_step)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.
OnURLFetchComplete(const net::URLFetcher * source)407 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
408 source->GetResponseAsString(&response_data_);
409 response_status_ = source->GetStatus().status();
410 StopUILoop();
411 }
412
response_data()413 const std::string& response_data() {
414 return response_data_;
415 }
416
417 private:
418 friend class base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>;
~SafeBrowsingServerTestHelper()419 virtual ~SafeBrowsingServerTestHelper() {}
420
421 // Stops UI loop after desired status is updated.
StopUILoop()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.
FetchUrl(const GURL & url)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
IN_PROC_BROWSER_TEST_F(SafeBrowsingServerTest,SafeBrowsingServerTest)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