This source file includes following definitions.
- GetWebContents
- ScheduleRemoveIframe
- SetUpCommandLine
- SetUpOnMainThread
- ExtractFeatures
- ExtractFeaturesInternal
- ExtractionDone
- RemoveIframe
- StartTestServer
- HandleRequest
- GetURL
- LoadHtml
- 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_dom_feature_extractor.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/renderer/safe_browsing/features.h"
#include "chrome/renderer/safe_browsing/mock_feature_extractor_clock.h"
#include "chrome/renderer/safe_browsing/test_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/interstitial_page.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_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/WebString.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebScriptSource.h"
#include "third_party/WebKit/public/web/WebView.h"
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
namespace {
const int kRenderViewRoutingId = 2;
}
namespace safe_browsing {
class PhishingDOMFeatureExtractorTest : public InProcessBrowserTest {
public:
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void ScheduleRemoveIframe() {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&PhishingDOMFeatureExtractorTest::RemoveIframe,
weak_factory_.GetWeakPtr()));
}
protected:
PhishingDOMFeatureExtractorTest() : weak_factory_(this) {}
virtual ~PhishingDOMFeatureExtractorTest() {}
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 {
extractor_.reset(new PhishingDOMFeatureExtractor(
content::RenderView::FromRoutingID(kRenderViewRoutingId), &clock_));
ASSERT_TRUE(StartTestServer());
host_resolver()->AddRule("*", "127.0.0.1");
}
bool ExtractFeatures(FeatureMap* features) {
success_ = false;
PostTaskToInProcessRendererAndWait(
base::Bind(&PhishingDOMFeatureExtractorTest::ExtractFeaturesInternal,
base::Unretained(this),
features));
return success_;
}
void ExtractFeaturesInternal(FeatureMap* features) {
scoped_refptr<content::MessageLoopRunner> message_loop =
new content::MessageLoopRunner;
extractor_->ExtractFeatures(
features,
base::Bind(&PhishingDOMFeatureExtractorTest::ExtractionDone,
base::Unretained(this),
message_loop->QuitClosure()));
message_loop->Run();
}
void ExtractionDone(const base::Closure& quit_closure,
bool success) {
success_ = success;
quit_closure.Run();
}
void RemoveIframe() {
content::RenderView* render_view =
content::RenderView::FromRoutingID(kRenderViewRoutingId);
blink::WebFrame* main_frame = render_view->GetWebView()->mainFrame();
ASSERT_TRUE(main_frame);
main_frame->executeScript(
blink::WebString(
"document.body.removeChild(document.getElementById('frame1'));"));
}
bool StartTestServer() {
CHECK(!embedded_test_server_);
embedded_test_server_.reset(new net::test_server::EmbeddedTestServer());
embedded_test_server_->RegisterRequestHandler(
base::Bind(&PhishingDOMFeatureExtractorTest::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;
std::map<std::string, std::string>::const_iterator it =
responses_.find(url);
if (it == responses_.end())
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(it->second);
return http_response.PassAs<net::test_server::HttpResponse>();
}
GURL GetURL(const std::string& host, const std::string& path) {
GURL::Replacements replace;
replace.SetHostStr(host);
replace.SetPathStr(path);
return embedded_test_server_->base_url().ReplaceComponents(replace);
}
GURL LoadHtml(const std::string& host, const std::string& content) {
GURL url(GetURL(host, ""));
responses_[url.spec()] = content;
ui_test_utils::NavigateToURL(browser(), url);
return url;
}
std::map<std::string, std::string> responses_;
scoped_ptr<net::test_server::EmbeddedTestServer> embedded_test_server_;
MockFeatureExtractorClock clock_;
scoped_ptr<PhishingDOMFeatureExtractor> extractor_;
bool success_;
base::WeakPtrFactory<PhishingDOMFeatureExtractorTest> weak_factory_;
};
IN_PROC_BROWSER_TEST_F(PhishingDOMFeatureExtractorTest, FormFeatures) {
EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
FeatureMap expected_features;
expected_features.AddBooleanFeature(features::kPageHasForms);
expected_features.AddRealFeature(features::kPageActionOtherDomainFreq, 0.25);
expected_features.AddBooleanFeature(features::kPageHasTextInputs);
expected_features.AddBooleanFeature(features::kPageHasCheckInputs);
FeatureMap features;
LoadHtml(
"host.com",
"<html><head><body>"
"<form action=\"query\"><input type=text><input type=checkbox></form>"
"<form action=\"http://cgi.host.com/submit\"></form>"
"<form action=\"http://other.com/\"></form>"
"<form action=\"query\"></form>"
"<form></form></body></html>");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
expected_features.Clear();
expected_features.AddBooleanFeature(features::kPageHasRadioInputs);
expected_features.AddBooleanFeature(features::kPageHasPswdInputs);
features.Clear();
LoadHtml(
"host.com",
"<html><head><body>"
"<input type=\"radio\"><input type=password></body></html>");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
expected_features.Clear();
expected_features.AddBooleanFeature(features::kPageHasTextInputs);
features.Clear();
LoadHtml(
"host.com",
"<html><head><body><input></body></html>");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
expected_features.Clear();
expected_features.AddBooleanFeature(features::kPageHasTextInputs);
features.Clear();
LoadHtml(
"host.com",
"<html><head><body><input type=\"invalid\"></body></html>");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
}
IN_PROC_BROWSER_TEST_F(PhishingDOMFeatureExtractorTest, LinkFeatures) {
EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
FeatureMap expected_features;
expected_features.AddRealFeature(features::kPageExternalLinksFreq, 0.5);
expected_features.AddRealFeature(features::kPageSecureLinksFreq, 0.0);
expected_features.AddBooleanFeature(features::kPageLinkDomain +
std::string("chromium.org"));
FeatureMap features;
LoadHtml(
"www.host.com",
"<html><head><body>"
"<a href=\"http://www2.host.com/abc\">link</a>"
"<a name=page_anchor></a>"
"<a href=\"http://www.chromium.org/\">chromium</a>"
"</body></html");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
expected_features.Clear();
expected_features.AddRealFeature(features::kPageExternalLinksFreq, 0.25);
expected_features.AddRealFeature(features::kPageSecureLinksFreq, 0.5);
expected_features.AddBooleanFeature(features::kPageLinkDomain +
std::string("chromium.org"));
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
ASSERT_TRUE(https_server.Start());
std::string url_str = "https://host.com:";
url_str += base::IntToString(https_server.host_port_pair().port());
url_str += "/files/safe_browsing/secure_link_features.html";
ui_test_utils::NavigateToURL(browser(), GURL(url_str));
content::InterstitialPage* interstitial_page =
GetWebContents()->GetInterstitialPage();
interstitial_page->Proceed();
content::WaitForLoadStop(GetWebContents());
features.Clear();
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
}
IN_PROC_BROWSER_TEST_F(PhishingDOMFeatureExtractorTest,
ScriptAndImageFeatures) {
EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
FeatureMap expected_features;
expected_features.AddBooleanFeature(features::kPageNumScriptTagsGTOne);
FeatureMap features;
LoadHtml(
"host.com",
"<html><head><script></script><script></script></head></html>");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
expected_features.Clear();
expected_features.AddBooleanFeature(features::kPageNumScriptTagsGTOne);
expected_features.AddBooleanFeature(features::kPageNumScriptTagsGTSix);
expected_features.AddRealFeature(features::kPageImgOtherDomainFreq, 0.5);
features.Clear();
net::SpawnedTestServer https_server(
net::SpawnedTestServer::TYPE_HTTPS,
net::SpawnedTestServer::kLocalhost,
base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
ASSERT_TRUE(https_server.Start());
std::string url_str = "https://host.com:";
url_str += base::IntToString(https_server.host_port_pair().port());
url_str += "/files/safe_browsing/secure_script_and_image.html";
ui_test_utils::NavigateToURL(browser(), GURL(url_str));
content::InterstitialPage* interstitial_page =
GetWebContents()->GetInterstitialPage();
interstitial_page->Proceed();
content::WaitForLoadStop(GetWebContents());
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
}
IN_PROC_BROWSER_TEST_F(PhishingDOMFeatureExtractorTest, SubFrames) {
EXPECT_CALL(clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
std::string port = base::IntToString(embedded_test_server_->port());
responses_[GetURL("host2.com", "").spec()] =
"<html><head><script></script><body>"
"<form action=\"http://host4.com/\"><input type=checkbox></form>"
"<form action=\"http://host2.com/submit\"></form>"
"<a href=\"http://www.host2.com/home\">link</a>"
"<iframe src=\"nested.html\"></iframe>"
"<body></html>";
responses_[GetURL("host2.com", "nested.html").spec()] =
"<html><body><input type=password>"
"<a href=\"https://host4.com/\">link</a>"
"<a href=\"relative\">another</a>"
"</body></html>";
responses_[GetURL("host3.com", "").spec()] =
"<html><head><script></script><body>"
"<img src=\"http://host.com/123.png\">"
"</body></html>";
FeatureMap expected_features;
expected_features.AddBooleanFeature(features::kPageHasForms);
expected_features.AddRealFeature(features::kPageActionOtherDomainFreq, 0.5);
expected_features.AddBooleanFeature(features::kPageHasTextInputs);
expected_features.AddBooleanFeature(features::kPageHasPswdInputs);
expected_features.AddBooleanFeature(features::kPageHasCheckInputs);
expected_features.AddRealFeature(features::kPageExternalLinksFreq, 0.25);
expected_features.AddBooleanFeature(features::kPageLinkDomain +
std::string("host4.com"));
expected_features.AddRealFeature(features::kPageSecureLinksFreq, 0.25);
expected_features.AddBooleanFeature(features::kPageNumScriptTagsGTOne);
expected_features.AddRealFeature(features::kPageImgOtherDomainFreq, 1.0);
FeatureMap features;
std::string html(
"<html><body><input type=text><a href=\"info.html\">link</a>"
"<iframe src=\"http://host2.com:");
html += port;
html += std::string(
"/\"></iframe>"
"<iframe src=\"http://host3.com:");
html += port;
html += std::string("/\"></iframe></body></html>");
LoadHtml("host.com", html);
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
}
IN_PROC_BROWSER_TEST_F(PhishingDOMFeatureExtractorTest, Continuation) {
std::string response = "<html><head></head><body>"
"<form action=\"ondomain\"></form>";
for (int i = 0; i < 45; ++i) {
response.append("<p>");
}
response.append("<form action=\"http://host2.com/\"></form></body></html>");
base::TimeTicks now = base::TimeTicks::Now();
EXPECT_CALL(clock_, Now())
.WillOnce(Return(now))
.WillOnce(Return(now))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(6)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(12)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(22)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(24)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(30)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(36)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(46)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(48)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(54)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(56)));
FeatureMap expected_features;
expected_features.AddBooleanFeature(features::kPageHasForms);
expected_features.AddRealFeature(features::kPageActionOtherDomainFreq, 0.5);
FeatureMap features;
LoadHtml("host.com", response);
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
::testing::Mock::VerifyAndClearExpectations(&clock_);
EXPECT_CALL(clock_, Now())
.WillOnce(Return(now))
.WillOnce(Return(now))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(300)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(350)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(360)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(600)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(620)));
features.Clear();
EXPECT_FALSE(ExtractFeatures(&features));
}
IN_PROC_BROWSER_TEST_F(PhishingDOMFeatureExtractorTest, SubframeRemoval) {
responses_[GetURL("host.com", "frame.html").spec()] =
"<html><body><p><p><p><input type=password></body></html>";
base::TimeTicks now = base::TimeTicks::Now();
EXPECT_CALL(clock_, Now())
.WillOnce(Return(now))
.WillOnce(Return(now))
.WillOnce(DoAll(
Invoke(this, &PhishingDOMFeatureExtractorTest::ScheduleRemoveIframe),
Return(now + base::TimeDelta::FromMilliseconds(21))))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(25)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(27)))
.WillOnce(Return(now + base::TimeDelta::FromMilliseconds(33)));
FeatureMap expected_features;
expected_features.AddBooleanFeature(features::kPageHasForms);
expected_features.AddBooleanFeature(features::kPageHasPswdInputs);
FeatureMap features;
LoadHtml(
"host.com",
"<html><head></head><body>"
"<iframe src=\"frame.html\" id=\"frame1\"></iframe>"
"<form></form></body></html>");
ASSERT_TRUE(ExtractFeatures(&features));
ExpectFeatureMapsAreEqual(features, expected_features);
}
}