This source file includes following definitions.
- waiting_message_loop_
- verdict
- OnMessageReceived
- Reset
- RunUntilVerdictReceived
- OnPhishingDetectionDone
- CancelCalled
- SetUpCommandLine
- SetUpOnMainThread
- RunClassificationDone
- OnStartPhishingDetection
- PageCaptured
- StartTestServer
- HandleRequest
- GetWebContents
- LoadHtml
- NavigateMainFrame
- NavigateMainFrameInternal
- GoBack
- GoForward
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
- IN_PROC_BROWSER_TEST_F
#include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h"
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/safe_browsing/csd.pb.h"
#include "chrome/common/safe_browsing/safebrowsing_messages.h"
#include "chrome/renderer/safe_browsing/features.h"
#include "chrome/renderer/safe_browsing/phishing_classifier.h"
#include "chrome/renderer/safe_browsing/scorer.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/renderer/render_view.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
using ::testing::_;
using ::testing::InSequence;
using ::testing::Mock;
using ::testing::Pointee;
using ::testing::StrictMock;
namespace safe_browsing {
namespace {
const int kRenderViewRoutingId = 2;
class MockPhishingClassifier : public PhishingClassifier {
public:
explicit MockPhishingClassifier(content::RenderView* render_view)
: PhishingClassifier(render_view, NULL ) {}
virtual ~MockPhishingClassifier() {}
MOCK_METHOD2(BeginClassification,
void(const base::string16*, const DoneCallback&));
MOCK_METHOD0(CancelPendingClassification, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockPhishingClassifier);
};
class MockScorer : public Scorer {
public:
MockScorer() : Scorer() {}
virtual ~MockScorer() {}
MOCK_CONST_METHOD1(ComputeScore, double(const FeatureMap&));
private:
DISALLOW_COPY_AND_ASSIGN(MockScorer);
};
class InterceptingMessageFilter : public content::BrowserMessageFilter {
public:
InterceptingMessageFilter()
: BrowserMessageFilter(SafeBrowsingMsgStart),
waiting_message_loop_(NULL) {
}
const ClientPhishingRequest* verdict() const { return verdict_.get(); }
virtual bool OnMessageReceived(const IPC::Message& message,
bool* message_was_ok) OVERRIDE {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(InterceptingMessageFilter, message)
IPC_MESSAGE_HANDLER(SafeBrowsingHostMsg_PhishingDetectionDone,
OnPhishingDetectionDone)
IPC_MESSAGE_UNHANDLED(handled = false);
IPC_END_MESSAGE_MAP()
return handled;
}
void Reset() {
run_loop_.reset(new base::RunLoop());
waiting_message_loop_ = base::MessageLoop::current();
quit_closure_ = run_loop_->QuitClosure();
}
void RunUntilVerdictReceived() {
content::RunThisRunLoop(run_loop_.get());
waiting_message_loop_ = NULL;
quit_closure_.Reset();
run_loop_.reset();
}
void OnPhishingDetectionDone(const std::string& verdict_str) {
scoped_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest);
if (verdict->ParseFromString(verdict_str) &&
verdict->IsInitialized()) {
verdict_.swap(verdict);
}
waiting_message_loop_->PostTask(FROM_HERE, quit_closure_);
}
private:
virtual ~InterceptingMessageFilter() {}
scoped_ptr<ClientPhishingRequest> verdict_;
base::MessageLoop* waiting_message_loop_;
base::Closure quit_closure_;
scoped_ptr<base::RunLoop> run_loop_;
};
}
class PhishingClassifierDelegateTest : public InProcessBrowserTest {
public:
void CancelCalled() {
if (runner_.get()) {
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, runner_->QuitClosure());
}
}
protected:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(switches::kSingleProcess);
#if defined(OS_WIN)
command_line->AppendSwitch(switches::kDisableAcceleratedCompositing);
#endif
}
virtual void SetUpOnMainThread() OVERRIDE {
intercepting_filter_ = new InterceptingMessageFilter();
content::RenderView* render_view =
content::RenderView::FromRoutingID(kRenderViewRoutingId);
GetWebContents()->GetRenderProcessHost()->AddFilter(
intercepting_filter_.get());
classifier_ = new StrictMock<MockPhishingClassifier>(render_view);
delegate_ = PhishingClassifierDelegate::Create(render_view, classifier_);
ASSERT_TRUE(StartTestServer());
host_resolver()->AddRule("*", "127.0.0.1");
}
void RunClassificationDone(const ClientPhishingRequest& verdict) {
intercepting_filter_->Reset();
PostTaskToInProcessRendererAndWait(
base::Bind(&PhishingClassifierDelegate::ClassificationDone,
base::Unretained(delegate_),
verdict));
intercepting_filter_->RunUntilVerdictReceived();
}
void OnStartPhishingDetection(const GURL& url) {
PostTaskToInProcessRendererAndWait(
base::Bind(&PhishingClassifierDelegate::OnStartPhishingDetection,
base::Unretained(delegate_), url));
}
void PageCaptured(base::string16* page_text, bool preliminary_capture) {
PostTaskToInProcessRendererAndWait(
base::Bind(&PhishingClassifierDelegate::PageCaptured,
base::Unretained(delegate_), page_text,
preliminary_capture));
}
bool StartTestServer() {
CHECK(!embedded_test_server_);
embedded_test_server_.reset(new net::test_server::EmbeddedTestServer());
embedded_test_server_->RegisterRequestHandler(
base::Bind(&PhishingClassifierDelegateTest::HandleRequest,
base::Unretained(this)));
return embedded_test_server_->InitializeAndWaitUntilReady();
}
scoped_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
std::map<std::string, std::string>::const_iterator host_it =
request.headers.find("Host");
if (host_it == request.headers.end())
return scoped_ptr<net::test_server::HttpResponse>();
std::string url =
std::string("http://") + host_it->second + request.relative_url;
if (response_url_.spec() != url)
return scoped_ptr<net::test_server::HttpResponse>();
scoped_ptr<net::test_server::BasicHttpResponse> http_response(
new net::test_server::BasicHttpResponse());
http_response->set_code(net::HTTP_OK);
http_response->set_content_type("text/html");
http_response->set_content(response_content_);
return http_response.PassAs<net::test_server::HttpResponse>();
}
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
GURL LoadHtml(const std::string& host, const std::string& content) {
GURL::Replacements replace_host;
replace_host.SetHostStr(host);
response_content_ = content;
response_url_ =
embedded_test_server_->base_url().ReplaceComponents(replace_host);
ui_test_utils::NavigateToURL(browser(), response_url_);
return response_url_;
}
void NavigateMainFrame(const GURL& url) {
PostTaskToInProcessRendererAndWait(
base::Bind(&PhishingClassifierDelegateTest::NavigateMainFrameInternal,
base::Unretained(this), url));
}
void NavigateMainFrameInternal(const GURL& url) {
content::RenderView* render_view =
content::RenderView::FromRoutingID(kRenderViewRoutingId);
render_view->GetWebView()->mainFrame()->firstChild()->loadRequest(
blink::WebURLRequest(url));
}
void GoBack() {
GetWebContents()->GetController().GoBack();
content::WaitForLoadStop(GetWebContents());
}
void GoForward() {
GetWebContents()->GetController().GoForward();
content::WaitForLoadStop(GetWebContents());
}
scoped_refptr<InterceptingMessageFilter> intercepting_filter_;
GURL response_url_;
std::string response_content_;
scoped_ptr<net::test_server::EmbeddedTestServer> embedded_test_server_;
scoped_ptr<ClientPhishingRequest> verdict_;
StrictMock<MockPhishingClassifier>* classifier_;
PhishingClassifierDelegate* delegate_;
scoped_refptr<content::MessageLoopRunner> runner_;
};
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, Navigation) {
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
ASSERT_TRUE(classifier_->is_ready());
EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2);
std::string port = base::IntToString(embedded_test_server_->port());
std::string html = "<html><body><iframe src=\"http://sub1.com:";
html += port;
html += "/\"></iframe></body></html>";
GURL url = LoadHtml("host.com", html);
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
base::string16 page_text = ASCIIToUTF16("dummy");
{
InSequence s;
EXPECT_CALL(*classifier_, CancelPendingClassification());
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
}
EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2);
content::TestNavigationObserver observer(GetWebContents());
chrome::Reload(browser(), CURRENT_TAB);
observer.Wait();
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
page_text = ASCIIToUTF16("dummy");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification())
.WillOnce(Invoke(this, &PhishingClassifierDelegateTest::CancelCalled));
runner_ = new content::MessageLoopRunner;
NavigateMainFrame(GURL(std::string("http://sub2.com:") + port + "/"));
runner_->Run();
runner_ = NULL;
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
page_text = ASCIIToUTF16("dummy");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
GURL foo_url = GURL(url.spec() + "#foo");
ui_test_utils::NavigateToURL(browser(), foo_url);
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
page_text = ASCIIToUTF16("dummy");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification())
.WillOnce(Invoke(this, &PhishingClassifierDelegateTest::CancelCalled));
runner_ = new content::MessageLoopRunner;
url = LoadHtml("host2.com", "dummy2");
runner_->Run();
runner_ = NULL;
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("dummy2");
OnStartPhishingDetection(url);
{
InSequence s;
EXPECT_CALL(*classifier_, CancelPendingClassification());
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
}
EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2);
GoBack();
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("dummy");
OnStartPhishingDetection(url);
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
GoForward();
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("dummy2");
OnStartPhishingDetection(url);
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification()).Times(2);
GoBack();
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("dummy");
OnStartPhishingDetection(url);
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
GURL foo2_url = GURL(foo_url.spec() + "2");
ui_test_utils::NavigateToURL(browser(), foo2_url);
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
page_text = ASCIIToUTF16("dummy");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, NoScorer) {
ASSERT_FALSE(classifier_->is_ready());
GURL url = LoadHtml("host.com", "dummy");
base::string16 page_text = ASCIIToUTF16("dummy");
OnStartPhishingDetection(url);
PageCaptured(&page_text, false);
url = LoadHtml("host2.com", "dummy2");
page_text = ASCIIToUTF16("dummy2");
OnStartPhishingDetection(url);
PageCaptured(&page_text, false);
page_text = ASCIIToUTF16("dummy2");
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
delegate_->SetPhishingScorer(&scorer);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, NoScorer_Ref) {
ASSERT_FALSE(classifier_->is_ready());
GURL url = LoadHtml("host.com", "dummy");
base::string16 page_text = ASCIIToUTF16("dummy");
OnStartPhishingDetection(url);
PageCaptured(&page_text, false);
OnStartPhishingDetection(url);
page_text = ASCIIToUTF16("dummy");
PageCaptured(&page_text, false);
page_text = ASCIIToUTF16("dummy");
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest,
NoStartPhishingDetection) {
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
ASSERT_TRUE(classifier_->is_ready());
EXPECT_CALL(*classifier_, CancelPendingClassification());
GURL url = LoadHtml("host.com", "<html><body>phish</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
base::string16 page_text = ASCIIToUTF16("phish");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("phish");
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
OnStartPhishingDetection(url);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
LoadHtml("host2.com", "<html><body>phish</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("phish");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
LoadHtml("host3.com", "<html><body>phish</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
EXPECT_CALL(*classifier_, CancelPendingClassification());
LoadHtml("host4.com", "<html><body>abc</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
page_text = ASCIIToUTF16("abc");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
ui_test_utils::NavigateToURL(
browser(), GURL("javascript:location.replace(\'redir\');"));
Mock::VerifyAndClearExpectations(classifier_);
std::string url_str = "http://host4.com:";
url_str += base::IntToString(embedded_test_server_->port());
url_str += "/redir";
OnStartPhishingDetection(GURL(url_str));
page_text = ASCIIToUTF16("123");
{
InSequence s;
EXPECT_CALL(*classifier_, CancelPendingClassification());
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
}
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest,
IgnorePreliminaryCapture) {
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
ASSERT_TRUE(classifier_->is_ready());
EXPECT_CALL(*classifier_, CancelPendingClassification());
GURL url = LoadHtml("host.com", "<html><body>phish</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
base::string16 page_text = ASCIIToUTF16("phish");
PageCaptured(&page_text, true);
page_text = ASCIIToUTF16("phish");
{
InSequence s;
EXPECT_CALL(*classifier_, CancelPendingClassification());
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
}
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, DuplicatePageCapture) {
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
ASSERT_TRUE(classifier_->is_ready());
EXPECT_CALL(*classifier_, CancelPendingClassification());
GURL url = LoadHtml("host.com", "<html><body>phish</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
OnStartPhishingDetection(url);
base::string16 page_text = ASCIIToUTF16("phish");
{
InSequence s;
EXPECT_CALL(*classifier_, CancelPendingClassification());
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
}
page_text = ASCIIToUTF16("phish");
EXPECT_CALL(*classifier_, CancelPendingClassification());
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
IN_PROC_BROWSER_TEST_F(PhishingClassifierDelegateTest, PhishingDetectionDone) {
MockScorer scorer;
delegate_->SetPhishingScorer(&scorer);
ASSERT_TRUE(classifier_->is_ready());
EXPECT_CALL(*classifier_, CancelPendingClassification());
GURL url = LoadHtml("host.com", "<html><body>phish</body></html>");
Mock::VerifyAndClearExpectations(classifier_);
base::string16 page_text = ASCIIToUTF16("phish");
OnStartPhishingDetection(url);
{
InSequence s;
EXPECT_CALL(*classifier_, CancelPendingClassification());
EXPECT_CALL(*classifier_, BeginClassification(Pointee(page_text), _));
PageCaptured(&page_text, false);
Mock::VerifyAndClearExpectations(classifier_);
}
ClientPhishingRequest verdict;
verdict.set_url(url.spec());
verdict.set_client_score(0.8f);
verdict.set_is_phishing(false);
RunClassificationDone(verdict);
ASSERT_TRUE(intercepting_filter_->verdict());
EXPECT_EQ(verdict.SerializeAsString(),
intercepting_filter_->verdict()->SerializeAsString());
EXPECT_CALL(*classifier_, CancelPendingClassification());
}
}