summaryrefslogtreecommitdiffstats
path: root/chrome/browser/safe_browsing
diff options
context:
space:
mode:
authornoelutz@chromium.org <noelutz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-28 17:00:59 +0000
committernoelutz@chromium.org <noelutz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-28 17:00:59 +0000
commit97b91aa50178b85af043ac9bf8685f3b783e8264 (patch)
treeb5c8513b7094a4ef8f81a07ba2a206ca36d94c0d /chrome/browser/safe_browsing
parent09f12a0b3a074b04d8b5d4b4151c8bb5b3205469 (diff)
downloadchromium_src-97b91aa50178b85af043ac9bf8685f3b783e8264.zip
chromium_src-97b91aa50178b85af043ac9bf8685f3b783e8264.tar.gz
chromium_src-97b91aa50178b85af043ac9bf8685f3b783e8264.tar.bz2
Separate pre-classification checks for client-side malware and phishing detection and enable the client-side malware
feature in all channels (not just dev and canary). BUG=352782 Review URL: https://codereview.chromium.org/173133004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260171 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/safe_browsing')
-rw-r--r--chrome/browser/safe_browsing/browser_feature_extractor.cc1
-rw-r--r--chrome/browser/safe_browsing/browser_feature_extractor.h6
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_host.cc383
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_host.h33
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_host_unittest.cc591
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service.cc9
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service_unittest.cc15
7 files changed, 554 insertions, 484 deletions
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.cc b/chrome/browser/safe_browsing/browser_feature_extractor.cc
index 522c3fc..8d0bd73 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.cc
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.cc
@@ -262,7 +262,6 @@ void BrowserFeatureExtractor::ExtractMalwareFeatures(
ClientMalwareRequest* request,
const MalwareDoneCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK_EQ(0U, request->url().find("http:"));
DCHECK(!callback.is_null());
// Grab the IPs because they might go away before we're done
diff --git a/chrome/browser/safe_browsing/browser_feature_extractor.h b/chrome/browser/safe_browsing/browser_feature_extractor.h
index 82a4e0d..bdad206 100644
--- a/chrome/browser/safe_browsing/browser_feature_extractor.h
+++ b/chrome/browser/safe_browsing/browser_feature_extractor.h
@@ -58,6 +58,9 @@ struct IPUrlInfo {
typedef std::map<std::string, std::vector<IPUrlInfo> > IPUrlMap;
struct BrowseInfo {
+ // The URL we're currently browsing.
+ GURL url;
+
// List of IPv4 and IPv6 addresses from which content was requested
// together with the hosts on it, while browsing to the |url|.
IPUrlMap ips;
@@ -78,6 +81,9 @@ struct BrowseInfo {
// The HTTP status code from this navigation.
int http_status_code;
+ // The page ID of the navigation. This comes from FrameNavigateParams.
+ int32 page_id;
+
BrowseInfo();
~BrowseInfo();
};
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
index 130f0ca..c2830fc 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -19,8 +19,6 @@
#include "chrome/browser/safe_browsing/client_side_detection_service.h"
#include "chrome/browser/safe_browsing/database_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/common/chrome_version_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/safe_browsing/csd.pb.h"
#include "chrome/common/safe_browsing/safebrowsing_messages.h"
@@ -36,6 +34,7 @@
#include "content/public/browser/resource_request_details.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/frame_navigate_params.h"
+#include "content/public/common/url_constants.h"
#include "url/gurl.h"
using content::BrowserThread;
@@ -50,28 +49,34 @@ const int ClientSideDetectionHost::kMaxIPsPerBrowse = 200;
const char kSafeBrowsingMatchKey[] = "safe_browsing_match";
+typedef base::Callback<void(bool)> ShouldClassifyUrlCallback;
+
// This class is instantiated each time a new toplevel URL loads, and
-// asynchronously checks whether the phishing classifier should run for this
-// URL. If so, it notifies the renderer with a StartPhishingDetection IPC.
-// Objects of this class are ref-counted and will be destroyed once nobody
-// uses it anymore. If |web_contents|, |csd_service| or |host| go away you need
-// to call Cancel(). We keep the |database_manager| alive in a ref pointer for
-// as long as it takes.
+// asynchronously checks whether the malware and phishing classifiers should run
+// for this URL. If so, it notifies the host class by calling the provided
+// callback form the UI thread. Objects of this class are ref-counted and will
+// be destroyed once nobody uses it anymore. If |web_contents|, |csd_service|
+// or |host| go away you need to call Cancel(). We keep the |database_manager|
+// alive in a ref pointer for as long as it takes.
class ClientSideDetectionHost::ShouldClassifyUrlRequest
: public base::RefCountedThreadSafe<
ClientSideDetectionHost::ShouldClassifyUrlRequest> {
public:
- ShouldClassifyUrlRequest(const content::FrameNavigateParams& params,
- WebContents* web_contents,
- ClientSideDetectionService* csd_service,
- SafeBrowsingDatabaseManager* database_manager,
- ClientSideDetectionHost* host)
- : canceled_(false),
- params_(params),
+ ShouldClassifyUrlRequest(
+ const content::FrameNavigateParams& params,
+ const ShouldClassifyUrlCallback& start_phishing_classification,
+ const ShouldClassifyUrlCallback& start_malware_classification,
+ WebContents* web_contents,
+ ClientSideDetectionService* csd_service,
+ SafeBrowsingDatabaseManager* database_manager,
+ ClientSideDetectionHost* host)
+ : params_(params),
web_contents_(web_contents),
csd_service_(csd_service),
database_manager_(database_manager),
- host_(host) {
+ host_(host),
+ start_phishing_classification_cb_(start_phishing_classification),
+ start_malware_classification_cb_(start_malware_classification) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(web_contents_);
DCHECK(csd_service_);
@@ -83,7 +88,8 @@ class ClientSideDetectionHost::ShouldClassifyUrlRequest
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// We start by doing some simple checks that can run on the UI thread.
- UMA_HISTOGRAM_COUNTS("SBClientPhishing.ClassificationStart", 1);
+ UMA_HISTOGRAM_BOOLEAN("SBClientPhishing.ClassificationStart", 1);
+ UMA_HISTOGRAM_BOOLEAN("SBClientMalware.ClassificationStart", 1);
// Only classify [X]HTML documents.
if (params_.contents_mime_type != "text/html" &&
@@ -91,50 +97,53 @@ class ClientSideDetectionHost::ShouldClassifyUrlRequest
VLOG(1) << "Skipping phishing classification for URL: " << params_.url
<< " because it has an unsupported MIME type: "
<< params_.contents_mime_type;
- UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
- NO_CLASSIFY_UNSUPPORTED_MIME_TYPE,
- NO_CLASSIFY_MAX);
- return;
+ DontClassifyForPhishing(NO_CLASSIFY_UNSUPPORTED_MIME_TYPE);
}
if (csd_service_->IsPrivateIPAddress(params_.socket_address.host())) {
VLOG(1) << "Skipping phishing classification for URL: " << params_.url
<< " because of hosting on private IP: "
<< params_.socket_address.host();
- UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
- NO_CLASSIFY_PRIVATE_IP,
- NO_CLASSIFY_MAX);
- return;
+ DontClassifyForPhishing(NO_CLASSIFY_PRIVATE_IP);
+ DontClassifyForMalware(NO_CLASSIFY_PRIVATE_IP);
}
- // Don't run the phishing classifier if the tab is incognito.
- if (web_contents_->GetBrowserContext()->IsOffTheRecord()) {
+ // For phishing we only classify HTTP pages.
+ if (!params_.url.SchemeIs(content::kHttpScheme)) {
VLOG(1) << "Skipping phishing classification for URL: " << params_.url
- << " because we're browsing incognito.";
- UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
- NO_CLASSIFY_OFF_THE_RECORD,
- NO_CLASSIFY_MAX);
+ << " because it is not HTTP: "
+ << params_.socket_address.host();
+ DontClassifyForPhishing(NO_CLASSIFY_NOT_HTTP_URL);
+ }
- return;
+ // Don't run any classifier if the tab is incognito.
+ if (web_contents_->GetBrowserContext()->IsOffTheRecord()) {
+ VLOG(1) << "Skipping phishing and malware classification for URL: "
+ << params_.url << " because we're browsing incognito.";
+ DontClassifyForPhishing(NO_CLASSIFY_OFF_THE_RECORD);
+ DontClassifyForMalware(NO_CLASSIFY_OFF_THE_RECORD);
}
// We lookup the csd-whitelist before we lookup the cache because
// a URL may have recently been whitelisted. If the URL matches
- // the csd-whitelist we won't start classification. The
+ // the csd-whitelist we won't start phishing classification. The
// csd-whitelist check has to be done on the IO thread because it
// uses the SafeBrowsing service class.
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&ShouldClassifyUrlRequest::CheckCsdWhitelist,
- this, params_.url));
+ if (ShouldClassifyForPhishing() || ShouldClassifyForMalware()) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&ShouldClassifyUrlRequest::CheckSafeBrowsingDatabase,
+ this, params_.url));
+ }
}
void Cancel() {
- canceled_ = true;
+ DontClassifyForPhishing(NO_CLASSIFY_CANCEL);
+ DontClassifyForMalware(NO_CLASSIFY_CANCEL);
// Just to make sure we don't do anything stupid we reset all these
// pointers except for the safebrowsing service class which may be
- // accessed by CheckCsdWhitelist().
+ // accessed by CheckSafeBrowsingDatabase().
web_contents_ = NULL;
csd_service_ = NULL;
host_ = NULL;
@@ -152,6 +161,11 @@ class ClientSideDetectionHost::ShouldClassifyUrlRequest
NO_CLASSIFY_MATCH_CSD_WHITELIST,
NO_CLASSIFY_TOO_MANY_REPORTS,
NO_CLASSIFY_UNSUPPORTED_MIME_TYPE,
+ NO_CLASSIFY_NO_DATABASE_MANAGER,
+ NO_CLASSIFY_KILLSWITCH,
+ NO_CLASSIFY_CANCEL,
+ NO_CLASSIFY_RESULT_FROM_CACHE,
+ NO_CLASSIFY_NOT_HTTP_URL,
NO_CLASSIFY_MAX // Always add new values before this one.
};
@@ -159,75 +173,120 @@ class ClientSideDetectionHost::ShouldClassifyUrlRequest
// The destructor can be called either from the UI or the IO thread.
virtual ~ShouldClassifyUrlRequest() { }
- void CheckCsdWhitelist(const GURL& url) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (!database_manager_.get() ||
- database_manager_->MatchCsdWhitelistUrl(url)) {
- // We're done. There is no point in going back to the UI thread.
- VLOG(1) << "Skipping phishing classification for URL: " << url
- << " because it matches the csd whitelist";
+ bool ShouldClassifyForPhishing() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return !start_phishing_classification_cb_.is_null();
+ }
+
+ bool ShouldClassifyForMalware() const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ return !start_malware_classification_cb_.is_null();
+ }
+
+ void DontClassifyForPhishing(PreClassificationCheckFailures reason) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (ShouldClassifyForPhishing()) {
+ // Track the first reason why we stopped classifying for phishing.
UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
- NO_CLASSIFY_MATCH_CSD_WHITELIST,
- NO_CLASSIFY_MAX);
- return;
+ reason, NO_CLASSIFY_MAX);
+ DVLOG(2) << "Failed phishing pre-classification checks. Reason: "
+ << reason;
+ start_phishing_classification_cb_.Run(false);
}
+ start_phishing_classification_cb_.Reset();
+ }
- bool malware_killswitch_on = database_manager_->IsMalwareKillSwitchOn();
+ void DontClassifyForMalware(PreClassificationCheckFailures reason) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (ShouldClassifyForMalware()) {
+ // Track the first reason why we stopped classifying for malware.
+ UMA_HISTOGRAM_ENUMERATION("SBClientMalware.PreClassificationCheckFail",
+ reason, NO_CLASSIFY_MAX);
+ DVLOG(2) << "Failed malware pre-classification checks. Reason: "
+ << reason;
+ start_malware_classification_cb_.Run(false);
+ }
+ start_malware_classification_cb_.Reset();
+ }
+ void CheckSafeBrowsingDatabase(const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // We don't want to call the classification callbacks from the IO
+ // thread so we simply pass the results of this method to CheckCache()
+ // which is called on the UI thread;
+ PreClassificationCheckFailures phishing_reason = NO_CLASSIFY_MAX;
+ PreClassificationCheckFailures malware_reason = NO_CLASSIFY_MAX;
+ if (!database_manager_.get()) {
+ // We cannot check the Safe Browsing whitelists so we stop here
+ // for safety.
+ malware_reason = phishing_reason = NO_CLASSIFY_NO_DATABASE_MANAGER;
+ } else {
+ if (database_manager_->MatchCsdWhitelistUrl(url)) {
+ VLOG(1) << "Skipping phishing classification for URL: " << url
+ << " because it matches the csd whitelist";
+ phishing_reason = NO_CLASSIFY_MATCH_CSD_WHITELIST;
+ }
+ if (database_manager_->IsMalwareKillSwitchOn()) {
+ malware_reason = NO_CLASSIFY_KILLSWITCH;
+ }
+ }
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
- base::Bind(&ShouldClassifyUrlRequest::CheckCache, this,
- malware_killswitch_on));
+ base::Bind(&ShouldClassifyUrlRequest::CheckCache,
+ this,
+ phishing_reason,
+ malware_reason));
}
- void CheckCache(bool malware_killswitch_on) {
+ void CheckCache(PreClassificationCheckFailures phishing_reason,
+ PreClassificationCheckFailures malware_reason) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (canceled_) {
- return;
+ if (phishing_reason != NO_CLASSIFY_MAX)
+ DontClassifyForPhishing(phishing_reason);
+ if (malware_reason != NO_CLASSIFY_MAX)
+ DontClassifyForMalware(malware_reason);
+ if (!ShouldClassifyForMalware() && !ShouldClassifyForPhishing()) {
+ return; // No point in doing anything else.
}
-
- host_->SetMalwareKillSwitch(malware_killswitch_on);
- // If result is cached, we don't want to run classification again
+ // If result is cached, we don't want to run classification again.
+ // In that case we're just trying to show the warning.
bool is_phishing;
if (csd_service_->GetValidCachedResult(params_.url, &is_phishing)) {
VLOG(1) << "Satisfying request for " << params_.url << " from cache";
- UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestSatisfiedFromCache", 1);
+ UMA_HISTOGRAM_BOOLEAN("SBClientPhishing.RequestSatisfiedFromCache", 1);
// Since we are already on the UI thread, this is safe.
host_->MaybeShowPhishingWarning(params_.url, is_phishing);
- return;
+ DontClassifyForPhishing(NO_CLASSIFY_RESULT_FROM_CACHE);
}
// We want to limit the number of requests, though we will ignore the
// limit for urls in the cache. We don't want to start classifying
// too many pages as phishing, but for those that we already think are
- // phishing we want to give ourselves a chance to fix false positives.
+ // phishing we want to send a request to the server to give ourselves
+ // a chance to fix misclassifications.
if (csd_service_->IsInCache(params_.url)) {
VLOG(1) << "Reporting limit skipped for " << params_.url
<< " as it was in the cache.";
- UMA_HISTOGRAM_COUNTS("SBClientPhishing.ReportLimitSkipped", 1);
+ UMA_HISTOGRAM_BOOLEAN("SBClientPhishing.ReportLimitSkipped", 1);
} else if (csd_service_->OverPhishingReportLimit()) {
VLOG(1) << "Too many report phishing requests sent recently, "
<< "not running classification for " << params_.url;
- UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
- NO_CLASSIFY_TOO_MANY_REPORTS,
- NO_CLASSIFY_MAX);
- return;
+ DontClassifyForPhishing(NO_CLASSIFY_TOO_MANY_REPORTS);
+ }
+ if (csd_service_->OverMalwareReportLimit()) {
+ DontClassifyForMalware(NO_CLASSIFY_TOO_MANY_REPORTS);
}
// Everything checks out, so start classification.
// |web_contents_| is safe to call as we will be destructed
// before it is.
- VLOG(1) << "Instruct renderer to start phishing detection for URL: "
- << params_.url;
- content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
- rvh->Send(new SafeBrowsingMsg_StartPhishingDetection(
- rvh->GetRoutingID(), params_.url));
+ if (ShouldClassifyForPhishing())
+ start_phishing_classification_cb_.Run(true);
+ if (ShouldClassifyForMalware())
+ start_malware_classification_cb_.Run(true);
}
- // No need to protect |canceled_| with a lock because it is only read and
- // written by the UI thread.
- bool canceled_;
content::FrameNavigateParams params_;
WebContents* web_contents_;
ClientSideDetectionService* csd_service_;
@@ -236,6 +295,9 @@ class ClientSideDetectionHost::ShouldClassifyUrlRequest
scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
ClientSideDetectionHost* host_;
+ ShouldClassifyUrlCallback start_phishing_classification_cb_;
+ ShouldClassifyUrlCallback start_malware_classification_cb_;
+
DISALLOW_COPY_AND_ASSIGN(ShouldClassifyUrlRequest);
};
@@ -248,10 +310,12 @@ ClientSideDetectionHost* ClientSideDetectionHost::Create(
ClientSideDetectionHost::ClientSideDetectionHost(WebContents* tab)
: content::WebContentsObserver(tab),
csd_service_(NULL),
+ classification_request_(NULL),
+ should_extract_malware_features_(true),
+ should_classify_for_malware_(false),
+ onload_complete_(false),
weak_factory_(this),
- unsafe_unique_page_id_(-1),
- malware_killswitch_on_(false),
- malware_report_enabled_(false) {
+ unsafe_unique_page_id_(-1) {
DCHECK(tab);
// Note: csd_service_ and sb_service will be NULL here in testing.
csd_service_ = g_browser_process->safe_browsing_detection_service();
@@ -266,13 +330,6 @@ ClientSideDetectionHost::ClientSideDetectionHost(WebContents* tab)
database_manager_ = sb_service->database_manager();
ui_manager_->AddObserver(this);
}
-
- // Only enable the malware bad IP matching and report feature for canary
- // and dev channel.
- chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
- malware_report_enabled_ = (
- channel == chrome::VersionInfo::CHANNEL_DEV ||
- channel == chrome::VersionInfo::CHANNEL_CANARY);
}
ClientSideDetectionHost::~ClientSideDetectionHost() {
@@ -302,6 +359,10 @@ void ClientSideDetectionHost::DidNavigateMainFrame(
// begin a new classification.
return;
}
+ // Cancel any pending classification request.
+ if (classification_request_.get()) {
+ classification_request_->Cancel();
+ }
// If we navigate away and there currently is a pending phishing
// report request we have to cancel it to make sure we don't display
// an interstitial for the wrong page. Note that this won't cancel
@@ -312,11 +373,6 @@ void ClientSideDetectionHost::DidNavigateMainFrame(
if (!csd_service_) {
return;
}
-
- // Cancel any pending classification request.
- if (classification_request_.get()) {
- classification_request_->Cancel();
- }
browse_info_.reset(new BrowseInfo);
// Store redirect chain information.
@@ -324,14 +380,25 @@ void ClientSideDetectionHost::DidNavigateMainFrame(
cur_host_ = params.url.host();
cur_host_redirects_ = params.redirects;
}
+ browse_info_->url = params.url;
browse_info_->host_redirects = cur_host_redirects_;
browse_info_->url_redirects = params.redirects;
browse_info_->referrer = params.referrer.url;
browse_info_->http_status_code = details.http_status_code;
+ browse_info_->page_id = params.page_id;
- // Notify the renderer if it should classify this URL.
+ should_extract_malware_features_ = true;
+ should_classify_for_malware_ = false;
+ onload_complete_ = false;
+
+ // Check whether we can cassify the current URL for phishing or malware.
classification_request_ = new ShouldClassifyUrlRequest(
- params, web_contents(), csd_service_, database_manager_.get(), this);
+ params,
+ base::Bind(&ClientSideDetectionHost::OnPhishingPreClassificationDone,
+ weak_factory_.GetWeakPtr()),
+ base::Bind(&ClientSideDetectionHost::OnMalwarePreClassificationDone,
+ weak_factory_.GetWeakPtr()),
+ web_contents(), csd_service_, database_manager_.get(), this);
classification_request_->Start();
}
@@ -414,6 +481,70 @@ void ClientSideDetectionHost::WebContentsDestroyed(WebContents* tab) {
feature_extractor_.reset();
}
+void ClientSideDetectionHost::OnPhishingPreClassificationDone(
+ bool should_classify) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (browse_info_.get() && should_classify) {
+ VLOG(1) << "Instruct renderer to start phishing detection for URL: "
+ << browse_info_->url;
+ content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
+ rvh->Send(new SafeBrowsingMsg_StartPhishingDetection(
+ rvh->GetRoutingID(), browse_info_->url));
+ }
+}
+
+void ClientSideDetectionHost::OnMalwarePreClassificationDone(
+ bool should_classify) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // If classification checks failed we should stop extracting malware features.
+ DVLOG(2) << "Malware pre-classification checks done. Should classify: "
+ << should_classify;
+ should_extract_malware_features_ = should_classify;
+ should_classify_for_malware_ = should_classify;
+ MaybeStartMalwareFeatureExtraction();
+}
+
+void ClientSideDetectionHost::DocumentOnLoadCompletedInMainFrame(
+ int32 page_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!csd_service_ || !browse_info_.get())
+ return;
+ DVLOG(2) << "Main frame onload hander called.";
+ if (browse_info_->page_id != page_id) {
+ // Something weird is happening here. The BrowseInfo page ID
+ // should always be the same as the most recent load.
+ UMA_HISTOGRAM_BOOLEAN("SBClientMalware.UnexpectedPageId", 1);
+ return;
+ }
+ onload_complete_ = true;
+ MaybeStartMalwareFeatureExtraction();
+}
+
+void ClientSideDetectionHost::MaybeStartMalwareFeatureExtraction() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (csd_service_ && browse_info_.get() &&
+ should_classify_for_malware_ &&
+ onload_complete_) {
+ scoped_ptr<ClientMalwareRequest> malware_request(
+ new ClientMalwareRequest);
+ // Start browser-side malware feature extraction. Once we're done it will
+ // send the malware client verdict request.
+ malware_request->set_url(browse_info_->url.spec());
+ const GURL& referrer = browse_info_->referrer;
+ if (referrer.SchemeIs("http")) { // Only send http urls.
+ malware_request->set_referrer_url(referrer.spec());
+ }
+ // This function doesn't expect browse_info_ to stay around after this
+ // function returns.
+ feature_extractor_->ExtractMalwareFeatures(
+ browse_info_.get(),
+ malware_request.release(),
+ base::Bind(&ClientSideDetectionHost::MalwareFeatureExtractionDone,
+ weak_factory_.GetWeakPtr()));
+ should_classify_for_malware_ = false;
+ }
+}
+
void ClientSideDetectionHost::OnPhishingDetectionDone(
const std::string& verdict_str) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -421,40 +552,15 @@ void ClientSideDetectionHost::OnPhishingDetectionDone(
// this method is called. The renderer should not start phishing detection
// if there isn't any service class in the browser.
DCHECK(csd_service_);
- // There shouldn't be any pending requests because we revoke them everytime
- // we navigate away.
- DCHECK(!weak_factory_.HasWeakPtrs());
DCHECK(browse_info_.get());
// We parse the protocol buffer here. If we're unable to parse it we won't
// send the verdict further.
scoped_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest);
if (csd_service_ &&
- !weak_factory_.HasWeakPtrs() &&
browse_info_.get() &&
verdict->ParseFromString(verdict_str) &&
verdict->IsInitialized()) {
- // We do the malware IP matching and request sending if the feature
- // is enabled.
- if (malware_report_enabled_ && !MalwareKillSwitchIsOn()) {
- scoped_ptr<ClientMalwareRequest> malware_verdict(
- new ClientMalwareRequest);
- // Start browser-side malware feature extraction. Once we're done it will
- // send the malware client verdict request.
- malware_verdict->set_url(verdict->url());
- const GURL& referrer = browse_info_->referrer;
- if (referrer.SchemeIs("http")) { // Only send http urls.
- malware_verdict->set_referrer_url(referrer.spec());
- }
- // This function doesn't expect browse_info_ to stay around after this
- // function returns.
- feature_extractor_->ExtractMalwareFeatures(
- browse_info_.get(),
- malware_verdict.release(),
- base::Bind(&ClientSideDetectionHost::MalwareFeatureExtractionDone,
- weak_factory_.GetWeakPtr()));
- }
-
// We only send phishing verdict to the server if the verdict is phishing or
// if a SafeBrowsing interstitial was already shown for this site. E.g., a
// malware or phishing interstitial was shown but the user clicked
@@ -472,14 +578,13 @@ void ClientSideDetectionHost::OnPhishingDetectionDone(
weak_factory_.GetWeakPtr()));
}
}
- browse_info_.reset();
}
void ClientSideDetectionHost::MaybeShowPhishingWarning(GURL phishing_url,
bool is_phishing) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- VLOG(2) << "Received server phishing verdict for URL:" << phishing_url
- << " is_phishing:" << is_phishing;
+ DVLOG(2) << "Received server phishing verdict for URL:" << phishing_url
+ << " is_phishing:" << is_phishing;
if (is_phishing) {
DCHECK(web_contents());
if (ui_manager_.get()) {
@@ -509,8 +614,8 @@ void ClientSideDetectionHost::MaybeShowMalwareWarning(GURL original_url,
GURL malware_url,
bool is_malware) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- VLOG(2) << "Received server malawre IP verdict for URL:" << malware_url
- << " is_malware:" << is_malware;
+ DVLOG(2) << "Received server malawre IP verdict for URL:" << malware_url
+ << " is_malware:" << is_malware;
if (is_malware && malware_url.is_valid() && original_url.is_valid()) {
DCHECK(web_contents());
if (ui_manager_.get()) {
@@ -540,8 +645,8 @@ void ClientSideDetectionHost::FeatureExtractionDone(
bool success,
ClientPhishingRequest* request) {
DCHECK(request);
- VLOG(2) << "Feature extraction done (success:" << success << ") for URL: "
- << request->url() << ". Start sending client phishing request.";
+ DVLOG(2) << "Feature extraction done (success:" << success << ") for URL: "
+ << request->url() << ". Start sending client phishing request.";
ClientSideDetectionService::ClientReportPhishingRequestCallback callback;
// If the client-side verdict isn't phishing we don't care about the server
// response because we aren't going to display a warning.
@@ -559,8 +664,8 @@ void ClientSideDetectionHost::MalwareFeatureExtractionDone(
bool feature_extraction_success,
scoped_ptr<ClientMalwareRequest> request) {
DCHECK(request.get());
- VLOG(2) << "Malware Feature extraction done for URL: " << request->url()
- << ", with badip url count:" << request->bad_ip_url_info_size();
+ DVLOG(2) << "Malware Feature extraction done for URL: " << request->url()
+ << ", with badip url count:" << request->bad_ip_url_info_size();
// Send ping if there is matching features.
if (feature_extraction_success && request->bad_ip_url_info_size() > 0) {
@@ -601,15 +706,13 @@ void ClientSideDetectionHost::Observe(
DCHECK_EQ(type, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED);
const ResourceRequestDetails* req = content::Details<ResourceRequestDetails>(
details).ptr();
- if (req && browse_info_.get() && malware_report_enabled_ &&
- !MalwareKillSwitchIsOn()) {
- if (req->url.is_valid()) {
- UpdateIPUrlMap(req->socket_address.host() /* ip */,
- req->url.spec() /* url */,
- req->method,
- req->referrer,
- req->resource_type);
- }
+ if (req && browse_info_.get() &&
+ should_extract_malware_features_ && req->url.is_valid()) {
+ UpdateIPUrlMap(req->socket_address.host() /* ip */,
+ req->url.spec() /* url */,
+ req->method,
+ req->referrer,
+ req->resource_type);
}
}
@@ -640,14 +743,4 @@ void ClientSideDetectionHost::set_safe_browsing_managers(
database_manager_ = database_manager;
}
-bool ClientSideDetectionHost::MalwareKillSwitchIsOn() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- return malware_killswitch_on_;
-}
-
-void ClientSideDetectionHost::SetMalwareKillSwitch(bool killswitch_on) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- malware_killswitch_on_ = killswitch_on;
-}
-
} // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.h b/chrome/browser/safe_browsing/client_side_detection_host.h
index c5cf95a..1492f13 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.h
+++ b/chrome/browser/safe_browsing/client_side_detection_host.h
@@ -79,6 +79,11 @@ class ClientSideDetectionHost : public content::WebContentsObserver,
class ShouldClassifyUrlRequest;
friend class ShouldClassifyUrlRequest;
+ // These methods are called when pre-classification checks are done for
+ // the phishing and malware clasifiers.
+ void OnPhishingPreClassificationDone(bool should_classify);
+ void OnMalwarePreClassificationDone(bool should_classify);
+
// Verdict is an encoded ClientPhishingRequest protocol message.
void OnPhishingDetectionDone(const std::string& verdict);
@@ -98,10 +103,14 @@ class ClientSideDetectionHost : public content::WebContentsObserver,
// the UI thread.
void FeatureExtractionDone(bool success, ClientPhishingRequest* request);
+ // Start malware classification once the onload handler was called and
+ // malware pre-classification checks are done and passed.
+ void MaybeStartMalwareFeatureExtraction();
+
// Function to be called when the browser malware feature extractor is done.
// Called on the UI thread.
- void MalwareFeatureExtractionDone(bool success,
- scoped_ptr<ClientMalwareRequest> request);
+ void MalwareFeatureExtractionDone(
+ bool success, scoped_ptr<ClientMalwareRequest> request);
// Update the entries in browse_info_->ips map.
void UpdateIPUrlMap(const std::string& ip,
@@ -116,6 +125,10 @@ class ClientSideDetectionHost : public content::WebContentsObserver,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE;
+ // Inherited from WebContentsObserver. This is called once the onload handler
+ // is called.
+ virtual void DocumentOnLoadCompletedInMainFrame(int32 page_id) OVERRIDE;
+
// Returns true if the user has seen a regular SafeBrowsing
// interstitial for the current page. This is only true if the user has
// actually clicked through the warning. This method is called on the UI
@@ -126,10 +139,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver,
// class.
void set_client_side_detection_service(ClientSideDetectionService* service);
- // Get/Set malware_killswitch_on_ value. These methods called on UI thread.
- bool MalwareKillSwitchIsOn();
- void SetMalwareKillSwitch(bool killswitch_on);
-
// This pointer may be NULL if client-side phishing detection is disabled.
ClientSideDetectionService* csd_service_;
// These pointers may be NULL if SafeBrowsing is disabled.
@@ -158,6 +167,10 @@ class ClientSideDetectionHost : public content::WebContentsObserver,
// Max number of urls we report for each malware IP.
static const int kMaxUrlsPerIP;
+ bool should_extract_malware_features_;
+ bool should_classify_for_malware_;
+ bool onload_complete_;
+
base::WeakPtrFactory<ClientSideDetectionHost> weak_factory_;
// Unique page ID of the most recent unsafe site that was loaded in this tab
@@ -165,14 +178,6 @@ class ClientSideDetectionHost : public content::WebContentsObserver,
int unsafe_unique_page_id_;
scoped_ptr<SafeBrowsingUIManager::UnsafeResource> unsafe_resource_;
- // Whether the malware IP matching feature killswitch is on.
- // This should be accessed from UI thread.
- bool malware_killswitch_on_;
-
- // Whether the malware bad ip matching and report feature is enabled.
- // This should be accessed from UI thread.
- bool malware_report_enabled_;
-
DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionHost);
};
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
index 3a66f8a..784182b 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -123,6 +123,7 @@ class MockClientSideDetectionService : public ClientSideDetectionService {
MOCK_METHOD2(GetValidCachedResult, bool(const GURL&, bool*));
MOCK_METHOD1(IsInCache, bool(const GURL&));
MOCK_METHOD0(OverPhishingReportLimit, bool());
+ MOCK_METHOD0(OverMalwareReportLimit, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
@@ -159,6 +160,7 @@ class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager {
MOCK_METHOD1(MatchCsdWhitelistUrl, bool(const GURL&));
MOCK_METHOD1(MatchMalwareIP, bool(const std::string& ip_address));
+ MOCK_METHOD0(IsMalwareKillSwitchOn, bool());
protected:
virtual ~MockSafeBrowsingDatabaseManager() {}
@@ -219,10 +221,6 @@ class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
// We need to create this here since we don't call
// DidNavigateMainFramePostCommit in this test.
csd_host_->browse_info_.reset(new BrowseInfo);
-
- // By default this is set to false. Turn it on as if we are in canary or
- // dev channel
- csd_host_->malware_report_enabled_ = true;
}
virtual void TearDown() {
@@ -248,6 +246,10 @@ class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
csd_host_->OnPhishingDetectionDone(verdict_str);
}
+ void DocumentOnLoadCompletedInMainFrame(int32 page_id) {
+ csd_host_->DocumentOnLoadCompletedInMainFrame(page_id);
+ }
+
void UpdateIPUrlMap(const std::string& ip, const std::string& host) {
csd_host_->UpdateIPUrlMap(ip, host, "", "", ResourceType::OBJECT);
}
@@ -260,9 +262,11 @@ class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
const bool* is_private,
const bool* is_incognito,
const bool* match_csd_whitelist,
+ const bool* malware_killswitch,
const bool* get_valid_cached_result,
const bool* is_in_cache,
- const bool* over_report_limit) {
+ const bool* over_phishing_report_limit,
+ const bool* over_malware_report_limit) {
if (is_private) {
EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
.WillOnce(Return(*is_private));
@@ -275,6 +279,10 @@ class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
EXPECT_CALL(*database_manager_.get(), MatchCsdWhitelistUrl(url))
.WillOnce(Return(*match_csd_whitelist));
}
+ if (malware_killswitch) {
+ EXPECT_CALL(*database_manager_.get(), IsMalwareKillSwitchOn())
+ .WillRepeatedly(Return(*malware_killswitch));
+ }
if (get_valid_cached_result) {
EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull()))
.WillOnce(DoAll(SetArgumentPointee<1>(true),
@@ -283,9 +291,13 @@ class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
if (is_in_cache) {
EXPECT_CALL(*csd_service_, IsInCache(url)).WillOnce(Return(*is_in_cache));
}
- if (over_report_limit) {
+ if (over_phishing_report_limit) {
EXPECT_CALL(*csd_service_, OverPhishingReportLimit())
- .WillOnce(Return(*over_report_limit));
+ .WillOnce(Return(*over_phishing_report_limit));
+ }
+ if (over_malware_report_limit) {
+ EXPECT_CALL(*csd_service_, OverMalwareReportLimit())
+ .WillOnce(Return(*over_malware_report_limit));
}
}
@@ -310,6 +322,25 @@ class ClientSideDetectionHostTest : public ChromeRenderViewHostTestHarness {
csd_host_->browse_info_->referrer = referrer;
}
+ void ExpectShouldClassifyForMalwareResult(bool should_classify) {
+ EXPECT_EQ(should_classify, csd_host_->should_classify_for_malware_);
+ }
+
+ void ExpectStartPhishingDetection(const GURL* url) {
+ const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
+ SafeBrowsingMsg_StartPhishingDetection::ID);
+ if (url) {
+ ASSERT_TRUE(msg);
+ Tuple1<GURL> actual_url;
+ SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
+ EXPECT_EQ(*url, actual_url.a);
+ EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
+ process()->sink().ClearMessages();
+ } else {
+ ASSERT_FALSE(msg);
+ }
+ }
+
void TestUnsafeResourceCopied(const UnsafeResource& resource) {
ASSERT_TRUE(csd_host_->unsafe_resource_.get());
// Test that the resource from OnSafeBrowsingHit notification was copied
@@ -450,13 +481,9 @@ TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneNotPhishing) {
verdict.set_client_score(1.0f);
verdict.set_is_phishing(true);
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
.WillOnce(DoAll(DeleteArg<1>(),
InvokeCallbackArgument<2>(true, &verdict)));
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
EXPECT_CALL(*csd_service_,
SendClientReportPhishingRequest(
Pointee(PartiallyEqualVerdict(verdict)), _))
@@ -494,14 +521,6 @@ TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneDisabled) {
SendClientReportPhishingRequest(
Pointee(PartiallyEqualVerdict(verdict)), _))
.WillOnce(SaveArg<1>(&cb));
-
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
- EXPECT_CALL(*csd_service_,
- SendClientReportMalwareRequest(_, _)).Times(0);
-
OnPhishingDetectionDone(verdict.SerializeAsString());
EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
ASSERT_FALSE(cb.is_null());
@@ -529,14 +548,9 @@ TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneShowInterstitial) {
verdict.set_client_score(1.0f);
verdict.set_is_phishing(true);
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
-
EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
.WillOnce(DoAll(DeleteArg<1>(),
InvokeCallbackArgument<2>(true, &verdict)));
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
EXPECT_CALL(*csd_service_,
SendClientReportPhishingRequest(
Pointee(PartiallyEqualVerdict(verdict)), _))
@@ -589,14 +603,9 @@ TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneMultiplePings) {
verdict.set_client_score(1.0f);
verdict.set_is_phishing(true);
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
-
EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
.WillOnce(DoAll(DeleteArg<1>(),
InvokeCallbackArgument<2>(true, &verdict)));
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
EXPECT_CALL(*csd_service_,
SendClientReportPhishingRequest(
Pointee(PartiallyEqualVerdict(verdict)), _))
@@ -613,7 +622,7 @@ TEST_F(ClientSideDetectionHostTest, OnPhishingDetectionDoneMultiplePings) {
csd_host_.get()));
GURL other_phishing_url("http://other_phishing_url.com/bla");
ExpectPreClassificationChecks(other_phishing_url, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse, &kFalse);
// We navigate away. The callback cb should be revoked.
NavigateAndCommit(other_phishing_url);
// Wait for the pre-classification checks to finish for other_phishing_url.
@@ -679,12 +688,7 @@ TEST_F(ClientSideDetectionHostTest,
verdict.set_client_score(0.1f);
verdict.set_is_phishing(false);
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
-
EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
OnPhishingDetectionDone(verdict.SerializeAsString());
EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
}
@@ -701,7 +705,7 @@ TEST_F(ClientSideDetectionHostTest,
// First we have to navigate to the URL to set the unique page ID.
ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
SetUnsafeSubResourceForCurrent();
@@ -727,7 +731,8 @@ TEST_F(ClientSideDetectionHostTest,
// Do an initial navigation to a safe host.
GURL start_url("http://safe.example.com/");
ExpectPreClassificationChecks(
- start_url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse);
+ start_url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse);
NavigateAndCommit(start_url);
WaitAndCheckPreClassificationChecks();
@@ -740,7 +745,7 @@ TEST_F(ClientSideDetectionHostTest,
verdict.set_is_phishing(false);
ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse);
NavigateWithSBHitAndCommit(url);
WaitAndCheckPreClassificationChecks();
@@ -756,11 +761,72 @@ TEST_F(ClientSideDetectionHostTest,
EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
ExpectPreClassificationChecks(start_url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse);
NavigateWithoutSBHitAndCommit(start_url);
WaitAndCheckPreClassificationChecks();
}
+TEST_F(ClientSideDetectionHostTest,
+ DocumentOnLoadCompletedInMainFrameShowMalwareInterstitial) {
+ // Case 9: client thinks the page match malware IP and so does the server.
+ // We show an sub-resource malware interstitial.
+ MockBrowserFeatureExtractor* mock_extractor =
+ new StrictMock<MockBrowserFeatureExtractor>(
+ web_contents(),
+ csd_host_.get());
+ SetFeatureExtractor(mock_extractor); // The host class takes ownership.
+
+ GURL malware_landing_url("http://malware.com/");
+ GURL malware_ip_url("http://badip.com");
+ ClientMalwareRequest malware_verdict;
+ malware_verdict.set_url("http://malware.com/");
+ ClientMalwareRequest::UrlInfo* badipurl =
+ malware_verdict.add_bad_ip_url_info();
+ badipurl->set_ip("1.2.3.4");
+ badipurl->set_url("http://badip.com");
+
+ ExpectPreClassificationChecks(GURL(malware_verdict.url()), &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse);
+ NavigateAndCommit(GURL(malware_verdict.url()));
+ WaitAndCheckPreClassificationChecks();
+
+ ClientSideDetectionService::ClientReportMalwareRequestCallback cb;
+ EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
+ .WillOnce(InvokeMalwareCallback(&malware_verdict));
+ EXPECT_CALL(*csd_service_,
+ SendClientReportMalwareRequest(
+ Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
+ .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
+ DocumentOnLoadCompletedInMainFrame(GetBrowseInfo()->page_id);
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+ ASSERT_FALSE(cb.is_null());
+
+ UnsafeResource resource;
+ EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
+ .WillOnce(SaveArg<0>(&resource));
+ cb.Run(malware_landing_url, malware_ip_url, true);
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
+ EXPECT_EQ(malware_ip_url, resource.url);
+ EXPECT_EQ(malware_landing_url, resource.original_url);
+ EXPECT_TRUE(resource.is_subresource);
+ EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, resource.threat_type);
+ EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
+ resource.render_process_host_id);
+ EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
+ resource.render_view_id);
+
+ // Make sure the client object will be deleted.
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
+ ui_manager_, resource.callback));
+}
+
TEST_F(ClientSideDetectionHostTest, UpdateIPUrlMap) {
BrowseInfo* browse_info = GetBrowseInfo();
@@ -819,180 +885,6 @@ TEST_F(ClientSideDetectionHostTest, UpdateIPUrlMap) {
browse_info->ips["100.100.100.256"]);
}
-TEST_F(ClientSideDetectionHostTest,
- OnPhishingDetectionDoneVerdictNotPhishingNotMalwareIP) {
- // Case 7: renderer sends a verdict string that isn't phishing and not matches
- // malware bad IP list
- MockBrowserFeatureExtractor* mock_extractor =
- new StrictMock<MockBrowserFeatureExtractor>(
- web_contents(),
- csd_host_.get());
- SetFeatureExtractor(mock_extractor); // The host class takes ownership.
-
- ClientPhishingRequest verdict;
- verdict.set_url("http://not-phishing.com/");
- verdict.set_client_score(0.1f);
- verdict.set_is_phishing(false);
-
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
-
- // That is a special case. If there were no IP matches or if feature
- // extraction failed the callback will delete the malware_verdict.
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
- EXPECT_CALL(*csd_service_,
- SendClientReportMalwareRequest(_, _)).Times(0);
- EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
-
- OnPhishingDetectionDone(verdict.SerializeAsString());
- EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
-}
-
-TEST_F(ClientSideDetectionHostTest,
- OnPhishingDetectionDoneVerdictNotPhishingButMalwareIP) {
- // Case 8: renderer sends a verdict string that isn't phishing but matches
- // malware bad IP list
- MockBrowserFeatureExtractor* mock_extractor =
- new StrictMock<MockBrowserFeatureExtractor>(
- web_contents(),
- csd_host_.get());
- SetFeatureExtractor(mock_extractor); // The host class takes ownership.
-
- ClientPhishingRequest verdict;
- verdict.set_url("http://not-phishing.com/");
- verdict.set_client_score(0.1f);
- verdict.set_is_phishing(false);
-
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
- malware_verdict.set_referrer_url("http://referrer.com/");
- ClientMalwareRequest::UrlInfo* badipurl =
- malware_verdict.add_bad_ip_url_info();
- badipurl->set_ip("1.2.3.4");
- badipurl->set_url("badip.com");
-
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
- EXPECT_CALL(*csd_service_,
- SendClientReportMalwareRequest(
- Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
- .WillOnce(DeleteArg<0>());
- EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _)).Times(0);
-
- SetReferrer(GURL("http://referrer.com/"));
- OnPhishingDetectionDone(verdict.SerializeAsString());
- EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
-}
-
-TEST_F(ClientSideDetectionHostTest,
- OnPhishingDetectionDoneVerdictPhishingAndMalwareIP) {
- // Case 9: renderer sends a verdict string that is phishing and matches
- // malware bad IP list
- MockBrowserFeatureExtractor* mock_extractor =
- new StrictMock<MockBrowserFeatureExtractor>(
- web_contents(),
- csd_host_.get());
- SetFeatureExtractor(mock_extractor); // The host class takes ownership.
-
- ClientSideDetectionService::ClientReportPhishingRequestCallback cb;
- ClientPhishingRequest verdict;
- verdict.set_url("http://not-phishing.com/");
- verdict.set_client_score(0.1f);
- verdict.set_is_phishing(true);
-
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url(verdict.url());
- ClientMalwareRequest::UrlInfo* badipurl =
- malware_verdict.add_bad_ip_url_info();
- badipurl->set_ip("1.2.3.4");
- badipurl->set_url("badip.com");
-
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
- EXPECT_CALL(*csd_service_,
- SendClientReportMalwareRequest(
- Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
- .WillOnce(DeleteArg<0>());
-
- EXPECT_CALL(*mock_extractor, ExtractFeatures(_, _, _))
- .WillOnce(DoAll(DeleteArg<1>(),
- InvokeCallbackArgument<2>(true, &verdict)));
-
- EXPECT_CALL(*csd_service_,
- SendClientReportPhishingRequest(
- Pointee(PartiallyEqualVerdict(verdict)), _))
- .WillOnce(SaveArg<1>(&cb));
-
- // Referrer url using https won't be set and sent out.
- SetReferrer(GURL("https://referrer.com/"));
- OnPhishingDetectionDone(verdict.SerializeAsString());
- EXPECT_TRUE(Mock::VerifyAndClear(mock_extractor));
- EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
- EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
- ASSERT_FALSE(cb.is_null());
-}
-
-TEST_F(ClientSideDetectionHostTest,
- OnPhishingDetectionDoneShowMalwareInterstitial) {
- // Case 10: client thinks the page match malware IP and so does the server.
- // We show an sub-resource malware interstitial.
- MockBrowserFeatureExtractor* mock_extractor =
- new StrictMock<MockBrowserFeatureExtractor>(
- web_contents(),
- csd_host_.get());
- SetFeatureExtractor(mock_extractor); // The host class takes ownership.
-
- ClientPhishingRequest verdict;
- verdict.set_url("http://not-phishing.com/");
- verdict.set_client_score(0.1f);
- verdict.set_is_phishing(false);
-
- ClientSideDetectionService::ClientReportMalwareRequestCallback cb;
- GURL malware_landing_url("http://malware.com/");
- GURL malware_ip_url("http://badip.com");
- ClientMalwareRequest malware_verdict;
- malware_verdict.set_url("http://malware.com/");
- ClientMalwareRequest::UrlInfo* badipurl =
- malware_verdict.add_bad_ip_url_info();
- badipurl->set_ip("1.2.3.4");
- badipurl->set_url("http://badip.com");
-
- EXPECT_CALL(*mock_extractor, ExtractMalwareFeatures(_, _, _))
- .WillOnce(InvokeMalwareCallback(&malware_verdict));
- EXPECT_CALL(*csd_service_,
- SendClientReportMalwareRequest(
- Pointee(PartiallyEqualMalwareVerdict(malware_verdict)), _))
- .WillOnce(DoAll(DeleteArg<0>(), SaveArg<1>(&cb)));
- OnPhishingDetectionDone(verdict.SerializeAsString());
- EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
- EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
- ASSERT_FALSE(cb.is_null());
-
- UnsafeResource resource;
- EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
- .WillOnce(SaveArg<0>(&resource));
- cb.Run(malware_landing_url, malware_ip_url, true);
-
- base::RunLoop().RunUntilIdle();
- EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
- EXPECT_EQ(malware_ip_url, resource.url);
- EXPECT_EQ(malware_landing_url, resource.original_url);
- EXPECT_TRUE(resource.is_subresource);
- EXPECT_EQ(SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL, resource.threat_type);
- EXPECT_EQ(web_contents()->GetRenderProcessHost()->GetID(),
- resource.render_process_host_id);
- EXPECT_EQ(web_contents()->GetRenderViewHost()->GetRoutingID(),
- resource.render_view_id);
-
- // Make sure the client object will be deleted.
- BrowserThread::PostTask(
- BrowserThread::IO,
- FROM_HERE,
- base::Bind(&MockSafeBrowsingUIManager::InvokeOnBlockingPageComplete,
- ui_manager_, resource.callback));
-}
-
TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
// Test that canceling pending should classify requests works as expected.
@@ -1006,10 +898,10 @@ TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_))
.WillOnce(Return(false))
.WillOnce(Return(false));
- ExpectPreClassificationChecks(first_url, NULL, &kFalse, &kFalse, NULL,
- NULL, NULL);
+ ExpectPreClassificationChecks(first_url, NULL, &kFalse, &kFalse, &kFalse,
+ NULL, NULL, NULL, NULL);
ExpectPreClassificationChecks(second_url, NULL, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse);
NavigateAndCommit(first_url);
// Don't flush the message loop, as we want to navigate to a different
@@ -1018,156 +910,243 @@ TEST_F(ClientSideDetectionHostTest, NavigationCancelsShouldClassifyUrl) {
WaitAndCheckPreClassificationChecks();
}
-TEST_F(ClientSideDetectionHostTest, ShouldClassifyUrl) {
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckPass) {
// Navigate the tab to a page. We should see a StartPhishingDetection IPC.
GURL url("http://host.com/");
ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_TRUE(msg);
- Tuple1<GURL> actual_url;
- SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
- EXPECT_EQ(url, actual_url.a);
- EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
- process()->sink().ClearMessages();
+ ExpectStartPhishingDetection(&url);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckInPageNavigation) {
+ GURL url("http://host.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse, &kFalse);
+ NavigateAndCommit(url);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(&url);
+ ExpectShouldClassifyForMalwareResult(true);
// Now try an in-page navigation. This should not trigger an IPC.
EXPECT_CALL(*csd_service_, IsPrivateIPAddress(_)).Times(0);
- url = GURL("http://host.com/#foo");
- ExpectPreClassificationChecks(url, NULL, NULL, NULL, NULL, NULL, NULL);
- NavigateAndCommit(url);
+ GURL inpage("http://host.com/#foo");
+ ExpectPreClassificationChecks(inpage, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL);
+ NavigateAndCommit(inpage);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_FALSE(msg);
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckXHTML) {
// Check that XHTML is supported, in addition to the default HTML type.
- // Note: for this test to work correctly, the new URL must be on the
- // same domain as the previous URL, otherwise it will create a new
- // RenderViewHost that won't have the mime type set.
- url = GURL("http://host.com/xhtml");
+ GURL url("http://host.com/xhtml");
rvh_tester()->SetContentsMimeType("application/xhtml+xml");
ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
+ &kFalse, &kFalse, &kFalse, &kFalse);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_TRUE(msg);
- SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
- EXPECT_EQ(url, actual_url.a);
- EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
- process()->sink().ClearMessages();
-
- // Navigate to a new host, which should cause another IPC.
- url = GURL("http://host2.com/");
- ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kFalse);
- NavigateAndCommit(url);
+
+ ExpectStartPhishingDetection(&url);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckTwoNavigations) {
+ // Navigate to two hosts, which should cause two IPCs.
+ GURL url1("http://host1.com/");
+ ExpectPreClassificationChecks(url1, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse, &kFalse);
+ NavigateAndCommit(url1);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_TRUE(msg);
- SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
- EXPECT_EQ(url, actual_url.a);
- EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
- process()->sink().ClearMessages();
- // If the mime type is not one that we support, no IPC should be triggered.
+ ExpectStartPhishingDetection(&url1);
+ ExpectShouldClassifyForMalwareResult(true);
+
+ GURL url2("http://host2.com/");
+ ExpectPreClassificationChecks(url2, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse, &kFalse);
+ NavigateAndCommit(url2);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(&url2);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckMimeType) {
+ // If the mime type is not one that we support, no IPC should be triggered
+ // but all pre-classification checks should run because we might classify
+ // other mime types for malware.
// Note: for this test to work correctly, the new URL must be on the
// same domain as the previous URL, otherwise it will create a new
// RenderViewHost that won't have the mime type set.
- url = GURL("http://host2.com/image.jpg");
+ GURL url("http://host2.com/image.jpg");
rvh_tester()->SetContentsMimeType("image/jpeg");
- ExpectPreClassificationChecks(url, NULL, NULL, NULL, NULL, NULL, NULL);
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse,&kFalse, &kFalse);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_FALSE(msg);
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckPrivateIpAddress) {
// If IsPrivateIPAddress returns true, no IPC should be triggered.
- url = GURL("http://host3.com/");
- ExpectPreClassificationChecks(url, &kTrue, NULL, NULL, NULL, NULL, NULL);
+ GURL url("http://host3.com/");
+ ExpectPreClassificationChecks(url, &kTrue, &kFalse, NULL, NULL, NULL, NULL,
+ NULL, NULL);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
+ const IPC::Message* msg = process()->sink().GetFirstMessageMatching(
SafeBrowsingMsg_StartPhishingDetection::ID);
ASSERT_FALSE(msg);
+ ExpectShouldClassifyForMalwareResult(false);
+}
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckIncognito) {
// If the tab is incognito there should be no IPC. Also, we shouldn't
// even check the csd-whitelist.
- url = GURL("http://host4.com/");
- ExpectPreClassificationChecks(url, &kFalse, &kTrue, NULL, NULL, NULL, NULL);
+ GURL url("http://host4.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kTrue, NULL, NULL, NULL, NULL,
+ NULL, NULL);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_FALSE(msg);
- // If the URL is on the csd whitelist, no IPC should be triggered.
- url = GURL("http://host5.com/");
- ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, NULL, NULL,
- NULL);
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(false);
+}
+
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckCsdWhitelist) {
+ // If the URL is on the csd whitelist no phishing IPC should be sent
+ // but we should classify the URL for malware.
+ GURL url("http://host5.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_FALSE(msg);
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckMalwareKillSwitch) {
+ // If the malware killswitch is on we shouldn't classify the page for malware.
+ GURL url("http://host5.com/kill-switch");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, &kFalse,
+ &kFalse, &kFalse, &kFalse);
+ NavigateAndCommit(url);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(&url);
+ ExpectShouldClassifyForMalwareResult(false);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckKillswitchAndCsdWhitelist) {
+ // If both the malware kill-swtich is on and the URL is on the csd whitelist,
+ // we will leave pre-classification checks early.
+ GURL url("http://host5.com/kill-switch-and-whitelisted");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kTrue, &kTrue, NULL,
+ NULL, NULL, NULL);
+ NavigateAndCommit(url);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(false);
+}
+
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckInvalidCache) {
// If item is in the cache but it isn't valid, we will classify regardless
// of whether we are over the reporting limit.
- url = GURL("http://host6.com/");
- ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kTrue,
- NULL);
+ GURL url("http://host6.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kTrue, NULL, &kFalse);
+
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_TRUE(msg);
- SafeBrowsingMsg_StartPhishingDetection::Read(msg, &actual_url);
- EXPECT_EQ(url, actual_url.a);
- EXPECT_EQ(rvh()->GetRoutingID(), msg->routing_id());
- process()->sink().ClearMessages();
+ ExpectStartPhishingDetection(&url);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckOverPhishingReportingLimit) {
// If the url isn't in the cache and we are over the reporting limit, we
// don't do classification.
- url = GURL("http://host7.com/");
+ GURL url("http://host7.com/");
ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
- &kFalse, &kTrue);
+ &kFalse, &kFalse, &kTrue, &kFalse);
NavigateAndCommit(url);
WaitAndCheckPreClassificationChecks();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_FALSE(msg);
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckOverMalwareReportingLimit) {
+ GURL url("http://host.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse, &kTrue);
+ NavigateAndCommit(url);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(&url);
+ ExpectShouldClassifyForMalwareResult(false);
+}
+
+TEST_F(ClientSideDetectionHostTest,
+ TestPreClassificationCheckOverBothReportingLimits) {
+ GURL url("http://host.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kTrue, &kTrue);
+ NavigateAndCommit(url);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(false);
+}
+
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckHttpsUrl) {
+ GURL url("https://host.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse,
+ &kFalse, &kFalse, &kFalse, &kFalse);
+ NavigateAndCommit(url);
+ WaitAndCheckPreClassificationChecks();
+
+ ExpectStartPhishingDetection(NULL);
+ ExpectShouldClassifyForMalwareResult(true);
+}
+
+TEST_F(ClientSideDetectionHostTest, TestPreClassificationCheckValidCached) {
// If result is cached, we will try and display the blocking page directly
// with no start classification message.
- url = GURL("http://host8.com/");
- ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, NULL,
- NULL);
+ GURL url("http://host8.com/");
+ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kTrue,
+ &kFalse, &kFalse, &kFalse);
UnsafeResource resource;
EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_))
.WillOnce(SaveArg<0>(&resource));
NavigateAndCommit(url);
- // Wait for CheckCsdWhitelist and CheckCache() to be called.
- base::RunLoop().RunUntilIdle();
- // Now we check that all expected functions were indeed called on the two
- // service objects.
- EXPECT_TRUE(Mock::VerifyAndClear(csd_host_.get()));
- EXPECT_TRUE(Mock::VerifyAndClear(ui_manager_.get()));
+ WaitAndCheckPreClassificationChecks();
EXPECT_EQ(url, resource.url);
EXPECT_EQ(url, resource.original_url);
- resource.callback.Reset();
- msg = process()->sink().GetFirstMessageMatching(
- SafeBrowsingMsg_StartPhishingDetection::ID);
- ASSERT_FALSE(msg);
+
+ ExpectStartPhishingDetection(NULL);
+
+ // Showing a phishing warning will invalidate all the weak pointers which
+ // means we will not extract malware features.
+ ExpectShouldClassifyForMalwareResult(false);
}
} // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc
index d63e45c..d045bd9 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -368,15 +368,6 @@ void ClientSideDetectionService::StartClientReportMalwareRequest(
return;
}
- if (OverMalwareReportLimit()) {
- UpdateEnumUMAHistogram(REPORT_HIT_LIMIT);
- DVLOG(1) << "Too many malware report requests sent recently."
- << "Skip sending malware report for " << GURL(request->url());
- if (!callback.is_null())
- callback.Run(GURL(request->url()), GURL(request->url()), false);
- return;
- }
-
std::string request_data;
if (!request->SerializeToString(&request_data)) {
UpdateEnumUMAHistogram(REPORT_FAILED_SERIALIZATION);
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
index b5f64d5..5762847 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc
@@ -472,25 +472,22 @@ TEST_F(ClientSideDetectionServiceTest, SendClientReportMalwareRequest) {
net::URLRequestStatus::FAILED);
EXPECT_FALSE(SendClientReportMalwareRequest(url));
- // server blacklist decision is false, and response is succesful
+ // Server blacklist decision is false, and response is successful
response.set_blacklist(false);
SetClientReportMalwareResponse(response.SerializeAsString(), net::HTTP_OK,
net::URLRequestStatus::SUCCESS);
EXPECT_FALSE(SendClientReportMalwareRequest(url));
- // Check that we have recorded all 4 requests within the correct time range.
+ // Check that we have recorded all 5 requests within the correct time range.
base::Time after = base::Time::Now();
std::queue<base::Time>& report_times = GetMalwareReportTimes();
- EXPECT_EQ(4U, report_times.size());
+ EXPECT_EQ(5U, report_times.size());
- // Another normal behavior will fail because of the limit is hit
- response.set_blacklist(true);
- SetClientReportMalwareResponse(response.SerializeAsString(), net::HTTP_OK,
- net::URLRequestStatus::SUCCESS);
- EXPECT_FALSE(SendClientReportMalwareRequest(url));
+ // Check that the malware report limit was reached.
+ EXPECT_TRUE(csd_service_->OverMalwareReportLimit());
report_times = GetMalwareReportTimes();
- EXPECT_EQ(4U, report_times.size());
+ EXPECT_EQ(5U, report_times.size());
while (!report_times.empty()) {
base::Time time = report_times.back();
report_times.pop();