blob: 383c6686a842252f0eea1af527e8a08856d108a1 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/safe_browsing/safe_browsing_util.h"
#include "chrome/browser/safe_browsing/ui_manager.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using content::BrowserThread;
static const char* kGoodURL = "https://www.good.com";
static const char* kBadURL = "https://www.malware.com";
static const char* kBadURLWithPath = "https://www.malware.com/index.html";
static const char* kAnotherBadURL = "https://www.badware.com";
static const char* kLandingURL = "https://www.landing.com";
namespace safe_browsing {
class SafeBrowsingCallbackWaiter {
public:
SafeBrowsingCallbackWaiter() {}
bool callback_called() const { return callback_called_; }
bool proceed() const { return proceed_; }
void OnBlockingPageDone(bool proceed) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
callback_called_ = true;
proceed_ = proceed;
loop_.Quit();
}
void OnBlockingPageDoneOnIO(bool proceed) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDone,
base::Unretained(this), proceed));
}
void WaitForCallback() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
loop_.Run();
}
private:
bool callback_called_ = false;
bool proceed_ = false;
base::RunLoop loop_;
};
class SafeBrowsingUIManagerTest : public ChromeRenderViewHostTestHarness {
public:
SafeBrowsingUIManagerTest() : ui_manager_(new SafeBrowsingUIManager(NULL)) {}
~SafeBrowsingUIManagerTest() override{};
void SetUp() override {
SetThreadBundleOptions(content::TestBrowserThreadBundle::REAL_IO_THREAD);
ChromeRenderViewHostTestHarness::SetUp();
}
void TearDown() override { ChromeRenderViewHostTestHarness::TearDown(); }
bool IsWhitelisted(SafeBrowsingUIManager::UnsafeResource resource) {
return ui_manager_->IsWhitelisted(resource);
}
void AddToWhitelist(SafeBrowsingUIManager::UnsafeResource resource) {
ui_manager_->AddToWhitelist(resource);
}
SafeBrowsingUIManager::UnsafeResource MakeUnsafeResource(
const char* url,
bool is_subresource) {
SafeBrowsingUIManager::UnsafeResource resource;
resource.url = GURL(url);
resource.is_subresource = is_subresource;
resource.render_process_host_id =
web_contents()->GetRenderProcessHost()->GetID();
resource.render_frame_id = web_contents()->GetMainFrame()->GetRoutingID();
resource.threat_type = SB_THREAT_TYPE_URL_MALWARE;
return resource;
}
SafeBrowsingUIManager::UnsafeResource MakeUnsafeResourceAndStartNavigation(
const char* url) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResource(url, false /* is_subresource */);
// The WC doesn't have a URL without a navigation. A main-frame malware
// unsafe resource must be a pending navigation.
content::WebContentsTester::For(web_contents())->StartNavigation(GURL(url));
return resource;
}
void SimulateBlockingPageDone(
const std::vector<SafeBrowsingUIManager::UnsafeResource>& resources,
bool proceed) {
ui_manager_->OnBlockingPageDone(resources, proceed);
}
private:
scoped_refptr<SafeBrowsingUIManager> ui_manager_;
};
TEST_F(SafeBrowsingUIManagerTest, Whitelist) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
AddToWhitelist(resource);
EXPECT_TRUE(IsWhitelisted(resource));
}
TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresSitesNotAdded) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kGoodURL);
EXPECT_FALSE(IsWhitelisted(resource));
}
TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresPath) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
AddToWhitelist(resource);
EXPECT_TRUE(IsWhitelisted(resource));
content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
SafeBrowsingUIManager::UnsafeResource resource_path =
MakeUnsafeResourceAndStartNavigation(kBadURLWithPath);
EXPECT_TRUE(IsWhitelisted(resource_path));
}
TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresThreatType) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
AddToWhitelist(resource);
EXPECT_TRUE(IsWhitelisted(resource));
SafeBrowsingUIManager::UnsafeResource resource_phishing =
MakeUnsafeResource(kBadURL, false /* is_subresource */);
resource_phishing.threat_type = SB_THREAT_TYPE_URL_PHISHING;
EXPECT_TRUE(IsWhitelisted(resource_phishing));
}
TEST_F(SafeBrowsingUIManagerTest, WhitelistWithUnrelatedPendingLoad) {
// Commit load of landing page.
NavigateAndCommit(GURL(kLandingURL));
{
// Simulate subresource malware hit on the landing page.
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResource(kBadURL, true /* is_subresource */);
// Start pending load to unrelated site.
content::WebContentsTester::For(web_contents())
->StartNavigation(GURL(kGoodURL));
// Whitelist the resource on the landing page.
AddToWhitelist(resource);
EXPECT_TRUE(IsWhitelisted(resource));
}
// Commit the pending load of unrelated site.
content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
{
// The unrelated site is not on the whitelist, even if the same subresource
// was on it.
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResource(kBadURL, true /* is_subresource */);
EXPECT_FALSE(IsWhitelisted(resource));
}
// Navigate back to the original landing url.
NavigateAndCommit(GURL(kLandingURL));
{
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResource(kBadURL, true /* is_subresource */);
// Original resource url is whitelisted.
EXPECT_TRUE(IsWhitelisted(resource));
}
{
// A different malware subresource on the same page is also whitelisted.
// (The whitelist is by the page url, not the resource url.)
SafeBrowsingUIManager::UnsafeResource resource2 =
MakeUnsafeResource(kAnotherBadURL, true /* is_subresource */);
EXPECT_TRUE(IsWhitelisted(resource2));
}
}
TEST_F(SafeBrowsingUIManagerTest, UICallbackProceed) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
SafeBrowsingCallbackWaiter waiter;
resource.callback =
base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDone,
base::Unretained(&waiter));
resource.callback_thread =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
resources.push_back(resource);
SimulateBlockingPageDone(resources, true);
EXPECT_TRUE(IsWhitelisted(resource));
waiter.WaitForCallback();
EXPECT_TRUE(waiter.callback_called());
EXPECT_TRUE(waiter.proceed());
}
TEST_F(SafeBrowsingUIManagerTest, UICallbackDontProceed) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
SafeBrowsingCallbackWaiter waiter;
resource.callback =
base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDone,
base::Unretained(&waiter));
resource.callback_thread =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
resources.push_back(resource);
SimulateBlockingPageDone(resources, false);
EXPECT_FALSE(IsWhitelisted(resource));
waiter.WaitForCallback();
EXPECT_TRUE(waiter.callback_called());
EXPECT_FALSE(waiter.proceed());
}
TEST_F(SafeBrowsingUIManagerTest, IOCallbackProceed) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
SafeBrowsingCallbackWaiter waiter;
resource.callback =
base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDoneOnIO,
base::Unretained(&waiter));
resource.callback_thread =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
resources.push_back(resource);
SimulateBlockingPageDone(resources, true);
EXPECT_TRUE(IsWhitelisted(resource));
waiter.WaitForCallback();
EXPECT_TRUE(waiter.callback_called());
EXPECT_TRUE(waiter.proceed());
}
TEST_F(SafeBrowsingUIManagerTest, IOCallbackDontProceed) {
SafeBrowsingUIManager::UnsafeResource resource =
MakeUnsafeResourceAndStartNavigation(kBadURL);
SafeBrowsingCallbackWaiter waiter;
resource.callback =
base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDoneOnIO,
base::Unretained(&waiter));
resource.callback_thread =
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
resources.push_back(resource);
SimulateBlockingPageDone(resources, false);
EXPECT_FALSE(IsWhitelisted(resource));
waiter.WaitForCallback();
EXPECT_TRUE(waiter.callback_called());
EXPECT_FALSE(waiter.proceed());
}
} // namespace safe_browsing