summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornoelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-17 19:47:13 +0000
committernoelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-17 19:47:13 +0000
commitfa685ffdc706738091a9243e12da2c89f2a01f0f (patch)
treee9635360daabc26604bbf842f9873c0ce12c19ac
parentbd6e45ffd4f1c0e805e76a472b50f8f05e3e82e0 (diff)
downloadchromium_src-fa685ffdc706738091a9243e12da2c89f2a01f0f.zip
chromium_src-fa685ffdc706738091a9243e12da2c89f2a01f0f.tar.gz
chromium_src-fa685ffdc706738091a9243e12da2c89f2a01f0f.tar.bz2
Intergration of the client-side phishing detection.
If the client-side phishing detection classifies a page as phishing it will send back a ping to Google to verify whether or not the page is really phishing. If the server also classifies the site as phishing we may show a phishing interstitial if it is enabled. BUG= TEST= Review URL: http://codereview.chromium.org/6014003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75299 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/task.h15
-rw-r--r--base/tuple.h7
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc8
-rw-r--r--chrome/browser/renderer_host/render_view_host.h1
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_host.cc162
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_host.h73
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_host_unittest.cc296
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service.cc3
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service.h11
-rw-r--r--chrome/browser/safe_browsing/safe_browsing_service.h16
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc3
-rw-r--r--chrome/browser/tab_contents/tab_contents.h11
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_common.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_switches.cc7
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/render_messages_internal.h7
-rw-r--r--chrome/common/safebrowsing_messages.cc8
-rw-r--r--chrome/common/safebrowsing_messages.h26
-rw-r--r--chrome/renderer/safe_browsing/phishing_classifier_delegate.cc5
-rw-r--r--chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc6
-rw-r--r--ipc/ipc_message_utils.h1
23 files changed, 637 insertions, 35 deletions
diff --git a/base/task.h b/base/task.h
index fc986b2..2deee56 100644
--- a/base/task.h
+++ b/base/task.h
@@ -423,6 +423,21 @@ inline CancelableTask* NewRunnableMethod(T* object, Method method,
e, f, g));
}
+template <class T, class Method, class A, class B, class C, class D, class E,
+ class F, class G, class H>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+ const A& a, const B& b,
+ const C& c, const D& d, const E& e,
+ const F& f, const G& g, const H& h) {
+ return new RunnableMethod<T,
+ Method,
+ Tuple8<A, B, C, D, E, F, G, H> >(object,
+ method,
+ MakeTuple(a, b, c,
+ d, e, f,
+ g, h));
+}
+
// RunnableFunction and NewRunnableFunction implementation ---------------------
template <class Function, class Params>
diff --git a/base/tuple.h b/base/tuple.h
index 13d8722..65d0908a 100644
--- a/base/tuple.h
+++ b/base/tuple.h
@@ -590,6 +590,13 @@ inline void DispatchToMethod(ObjT* obj, Method method,
(obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g);
}
+template<class ObjT, class Method, class A, class B, class C, class D, class E,
+ class F, class G, class H>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple8<A, B, C, D, E, F, G, H>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g, arg.h);
+}
+
// Static Dispatchers with no out params.
template <class Function>
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index 7611747..f868d4e 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -807,8 +807,6 @@ bool RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_WebDatabaseAccessed, OnWebDatabaseAccessed)
IPC_MESSAGE_HANDLER(ViewHostMsg_FocusedNodeChanged, OnMsgFocusedNodeChanged)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateZoomLimits, OnUpdateZoomLimits)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DetectedPhishingSite,
- OnDetectedPhishingSite)
IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse)
#if defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup)
@@ -1664,12 +1662,6 @@ void RenderViewHost::OnUpdateZoomLimits(int minimum_percent,
delegate_->UpdateZoomLimits(minimum_percent, maximum_percent, remember);
}
-void RenderViewHost::OnDetectedPhishingSite(const GURL& phishing_url,
- double phishing_score) {
- // TODO(noelutz): send an HTTP request to the client-side detection frontends
- // to confirm that the URL is really phishing.
-}
-
void RenderViewHost::OnScriptEvalResponse(int id, const ListValue& result) {
Value* result_value;
result.Get(0, &result_value);
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index 53f6a05..9a15718 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -640,7 +640,6 @@ class RenderViewHost : public RenderWidgetHost {
void OnUpdateZoomLimits(int minimum_percent,
int maximum_percent,
bool remember);
- void OnDetectedPhishingSite(const GURL& phishing_url, double phishing_score);
void OnScriptEvalResponse(int id, const ListValue& result);
void OnCommandStateChanged(int command,
bool is_enabled,
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc
new file mode 100644
index 0000000..b23a9bb
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/safe_browsing/client_side_detection_host.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/task.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/safebrowsing_messages.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/render_messages_params.h"
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_macros.h"
+
+namespace safe_browsing {
+
+// This class is used to display the phishing interstitial.
+class CsdClient : public SafeBrowsingService::Client {
+ public:
+ CsdClient() {}
+
+ // Method from SafeBrowsingService::Client. This method is called on the
+ // IO thread once the interstitial is going away. This method simply deletes
+ // the CsdClient object.
+ virtual void OnBlockingPageComplete(bool proceed) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ // Delete this on the UI thread since it was created there.
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ new DeleteTask<CsdClient>(this));
+ }
+
+ private:
+ friend class DeleteTask<CsdClient>; // Calls the private destructor.
+
+ // We're taking care of deleting this object. No-one else should delete
+ // this object.
+ virtual ~CsdClient() {}
+
+ DISALLOW_COPY_AND_ASSIGN(CsdClient);
+};
+
+ClientSideDetectionHost::ClientSideDetectionHost(TabContents* tab)
+ : TabContentsObserver(tab),
+ service_(g_browser_process->safe_browsing_detection_service()),
+ cb_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ DCHECK(tab);
+ // Note: service_ and sb_service_ might be NULL.
+ ResourceDispatcherHost* resource =
+ g_browser_process->resource_dispatcher_host();
+ if (resource) {
+ sb_service_ = resource->safe_browsing_service();
+ }
+}
+
+ClientSideDetectionHost::~ClientSideDetectionHost() {
+}
+
+bool ClientSideDetectionHost::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(ClientSideDetectionHost, message)
+ IPC_MESSAGE_HANDLER(SafeBrowsingDetectionHostMsg_DetectedPhishingSite,
+ OnDetectedPhishingSite)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void ClientSideDetectionHost::DidNavigateMainFramePostCommit(
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params) {
+ // TODO(noelutz): move this DCHECK to TabContents and fix all the unit tests
+ // that don't call this method on the UI thread.
+ // DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // 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
+ // the server ping back but only cancel the showing of the
+ // interstial.
+ cb_factory_.RevokeAll();
+}
+
+void ClientSideDetectionHost::OnDetectedPhishingSite(const GURL& phishing_url,
+ double phishing_score) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // There is something seriously wrong if there is no service class but
+ // this method is called. The renderer should not start phishing detection
+ // if there isn't any service class in the browser.
+ DCHECK(service_);
+ if (service_ && tab_contents()) {
+ // There shouldn't be any pending requests because we revoke them everytime
+ // we navigate away.
+ DCHECK(!cb_factory_.HasPendingCallbacks());
+ service_->SendClientReportPhishingRequest(
+ phishing_url,
+ phishing_score,
+ cb_factory_.NewCallback(
+ &ClientSideDetectionHost::MaybeShowPhishingWarning));
+ }
+}
+
+void ClientSideDetectionHost::MaybeShowPhishingWarning(GURL phishing_url,
+ bool is_phishing) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (is_phishing &&
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableClientSidePhishingInterstitial)) {
+ DCHECK(tab_contents());
+ // TODO(noelutz): this is not perfect. It's still possible that the
+ // user browses away before the interstitial is shown. Maybe we should
+ // stop all pending navigations?
+ if (sb_service_) {
+ // TODO(noelutz): refactor the SafeBrowsing service class and the
+ // SafeBrowsing blocking page class so that we don't need to depend
+ // on the SafeBrowsingService here and so that we don't need to go
+ // through the IO message loop.
+ std::vector<GURL> redirect_urls;
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(sb_service_.get(),
+ &SafeBrowsingService::DisplayBlockingPage,
+ phishing_url, phishing_url,
+ redirect_urls,
+ // We only classify the main frame URL.
+ ResourceType::MAIN_FRAME,
+ // TODO(noelutz): create a separate threat type
+ // for client-side phishing detection.
+ SafeBrowsingService::URL_PHISHING,
+ new CsdClient() /* will delete itself */,
+ tab_contents()->GetRenderProcessHost()->id(),
+ tab_contents()->render_view_host()->routing_id()));
+ }
+ }
+}
+
+void ClientSideDetectionHost::set_client_side_detection_service(
+ ClientSideDetectionService* service) {
+ service_ = service;
+}
+
+void ClientSideDetectionHost::set_safe_browsing_service(
+ SafeBrowsingService* service) {
+ sb_service_ = service;
+}
+
+} // 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
new file mode 100644
index 0000000..20c8eb7
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_host.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_H_
+#define CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/scoped_callback_factory.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/tab_contents/tab_contents_observer.h"
+#include "googleurl/src/gurl.h"
+
+class TabContents;
+
+namespace safe_browsing {
+
+class ClientSideDetectionService;
+
+// This class is used to receive the IPC from the renderer which
+// notifies the browser that a URL was classified as phishing. This
+// class relays this information to the client-side detection service
+// class which sends a ping to a server to validate the verdict.
+// TODO(noelutz): move all client-side detection IPCs to this class.
+class ClientSideDetectionHost : public TabContentsObserver {
+ public:
+ // The caller keeps ownership of the tab object and is responsible for
+ // ensuring that it stays valid for the entire lifetime of this object.
+ explicit ClientSideDetectionHost(TabContents* tab);
+ virtual ~ClientSideDetectionHost();
+
+ // From TabContentsObserver.
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // From TabContentsObserver. If we navigate away we cancel all pending
+ // callbacks that could show an interstitial.
+ virtual void DidNavigateMainFramePostCommit(
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params);
+
+ private:
+ friend class ClientSideDetectionHostTest;
+
+ void OnDetectedPhishingSite(const GURL& phishing_url, double phishing_score);
+
+ // Callback that is called when the server ping back is
+ // done. Display an interstitial if |is_phishing| is true.
+ // Otherwise, we do nothgin. Called in UI thread.
+ void MaybeShowPhishingWarning(GURL phishing_url, bool is_phishing);
+
+ // Used for testing. This function does not take ownership of the service
+ // class.
+ void set_client_side_detection_service(ClientSideDetectionService* service);
+
+ // Used for testing. This function does not take ownership of the service
+ // class.
+ void set_safe_browsing_service(SafeBrowsingService* service);
+
+ // This pointer may be NULL if client-side phishing detection is disabled.
+ ClientSideDetectionService* service_;
+ // This pointer may be NULL if SafeBrowsing is disabled.
+ scoped_refptr<SafeBrowsingService> sb_service_;
+
+ base::ScopedCallbackFactory<ClientSideDetectionHost> cb_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionHost);
+};
+
+} // namespace safe_browsing
+
+#endif // CHROME_BROWSER_SAFE_BROWSING_CLIENT_SIDE_DETECTION_HOST_H_
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
new file mode 100644
index 0000000..4ed2e25
--- /dev/null
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/scoped_temp_dir.h"
+#include "base/task.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/renderer_host/test/test_render_view_host.h"
+#include "chrome/browser/safe_browsing/client_side_detection_host.h"
+#include "chrome/browser/safe_browsing/client_side_detection_service.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/tab_contents/test_tab_contents.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/ui_test_utils.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock/include/gmock/gmock-spec-builders.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Mock;
+using ::testing::SaveArg;
+
+namespace safe_browsing {
+
+class MockClientSideDetectionService : public ClientSideDetectionService {
+ public:
+ MockClientSideDetectionService(const FilePath& model_path)
+ : ClientSideDetectionService(model_path, NULL) {}
+ virtual ~MockClientSideDetectionService() {};
+
+ MOCK_METHOD3(SendClientReportPhishingRequest,
+ void(const GURL&, double, ClientReportPhishingRequestCallback*));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService);
+};
+
+class MockSafeBrowsingService : public SafeBrowsingService {
+ public:
+ MockSafeBrowsingService() {}
+ virtual ~MockSafeBrowsingService() {}
+
+ MOCK_METHOD8(DisplayBlockingPage,
+ void(const GURL&, const GURL&, const std::vector<GURL>&,
+ ResourceType::Type, UrlCheckResult, Client*, int, int));
+
+ // Helper function which calls OnBlockingPageComplete for this client
+ // object.
+ void InvokeOnBlockingPageComplete(SafeBrowsingService::Client* client) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(client);
+ // Note: this will delete the client object in the case of the CsdClient
+ // implementation.
+ client->OnBlockingPageComplete(false);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService);
+};
+
+// Helper function which quits the UI message loop from the IO message loop.
+void QuitUIMessageLoop() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ new MessageLoop::QuitTask());
+}
+
+class ClientSideDetectionHostTest : public RenderViewHostTestHarness {
+ public:
+ virtual void SetUp() {
+ RenderViewHostTestHarness::SetUp();
+ ui_thread_.reset(new BrowserThread(BrowserThread::UI, &message_loop_));
+ // Note: we're starting a real IO thread to make sure our DCHECKs that
+ // verify which thread is running are actually tested.
+ io_thread_.reset(new BrowserThread(BrowserThread::IO));
+ ASSERT_TRUE(io_thread_->Start());
+
+ // Inject service classes.
+ ScopedTempDir tmp_dir;
+ ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
+ FilePath model_path = tmp_dir.path().AppendASCII("model");
+
+ csd_service_.reset(new MockClientSideDetectionService(model_path));
+ sb_service_ = new MockSafeBrowsingService();
+ csd_host_ = contents()->safebrowsing_detection_host();
+ csd_host_->set_client_side_detection_service(csd_service_.get());
+ csd_host_->set_safe_browsing_service(sb_service_.get());
+ }
+
+ virtual void TearDown() {
+ io_thread_.reset();
+ ui_thread_.reset();
+ RenderViewHostTestHarness::TearDown();
+ }
+
+ void OnDetectedPhishingSite(const GURL& phishing_url, double phishing_score) {
+ csd_host_->OnDetectedPhishingSite(phishing_url, phishing_score);
+ }
+
+ void FlushIOMessageLoop() {
+ // If there was a message posted on the IO thread to display the
+ // interstitial page we know that it would have been posted before
+ // we put the quit message there.
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableFunction(&QuitUIMessageLoop));
+ MessageLoop::current()->Run();
+ }
+
+ protected:
+ ClientSideDetectionHost* csd_host_;
+ scoped_ptr<MockClientSideDetectionService> csd_service_;
+ scoped_refptr<MockSafeBrowsingService> sb_service_;
+
+ private:
+ scoped_ptr<BrowserThread> ui_thread_;
+ scoped_ptr<BrowserThread> io_thread_;
+};
+
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteNotPhishing) {
+ // Case 1: client thinks the page is phishing. The server does not agree.
+ // No interstitial is shown.
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
+ GURL phishing_url("http://phishingurl.com/");
+
+ EXPECT_CALL(*csd_service_,
+ SendClientReportPhishingRequest(phishing_url, 1.0, _))
+ .WillOnce(SaveArg<2>(&cb));
+ OnDetectedPhishingSite(phishing_url, 1.0);
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+ ASSERT_TRUE(cb != NULL);
+
+ // Make sure DisplayBlockingPage is not going to be called.
+ EXPECT_CALL(*sb_service_,
+ DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0);
+ cb->Run(phishing_url, false);
+ delete cb;
+ // If there was a message posted on the IO thread to display the
+ // interstitial page we know that it would have been posted before
+ // we put the quit message there.
+ BrowserThread::PostTask(BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableFunction(&QuitUIMessageLoop));
+ MessageLoop::current()->Run();
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
+}
+
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteDisabled) {
+ // Case 2: client thinks the page is phishing and so does the server but
+ // showing the interstitial is disabled => no interstitial is shown.
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
+ GURL phishing_url("http://phishingurl.com/");
+
+ EXPECT_CALL(*csd_service_,
+ SendClientReportPhishingRequest(phishing_url, 1.0, _))
+ .WillOnce(SaveArg<2>(&cb));
+ OnDetectedPhishingSite(phishing_url, 1.0);
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+ ASSERT_TRUE(cb != NULL);
+
+ // Make sure DisplayBlockingPage is not going to be called.
+ EXPECT_CALL(*sb_service_,
+ DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0);
+ cb->Run(phishing_url, false);
+ delete cb;
+
+ FlushIOMessageLoop();
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
+}
+
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteShowInterstitial) {
+ // Case 3: client thinks the page is phishing and so does the server.
+ // We show an interstitial.
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
+ GURL phishing_url("http://phishingurl.com/");
+
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableClientSidePhishingInterstitial);
+
+ EXPECT_CALL(*csd_service_,
+ SendClientReportPhishingRequest(phishing_url, 1.0, _))
+ .WillOnce(SaveArg<2>(&cb));
+ OnDetectedPhishingSite(phishing_url, 1.0);
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+ ASSERT_TRUE(cb != NULL);
+
+ SafeBrowsingService::Client* client;
+ EXPECT_CALL(*sb_service_,
+ DisplayBlockingPage(
+ phishing_url,
+ phishing_url,
+ _,
+ ResourceType::MAIN_FRAME,
+ SafeBrowsingService::URL_PHISHING,
+ _ /* a CsdClient object */,
+ contents()->GetRenderProcessHost()->id(),
+ contents()->render_view_host()->routing_id()))
+ .WillOnce(SaveArg<5>(&client));
+
+ cb->Run(phishing_url, true);
+ delete cb;
+
+ FlushIOMessageLoop();
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
+
+ // Make sure the client object will be deleted.
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(
+ sb_service_.get(),
+ &MockSafeBrowsingService::InvokeOnBlockingPageComplete,
+ client));
+ // Since the CsdClient object will be deleted on the UI thread I need
+ // to run the UI message loop. Post a task to stop the UI message loop
+ // after the client object destructor is called.
+ FlushIOMessageLoop();
+}
+
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteMultiplePings) {
+ // Case 4 & 5: client thinks a page is phishing then navigates to
+ // another page which is also considered phishing by the client
+ // before the server responds with a verdict. After a while the
+ // server responds for both requests with a phishing verdict. Only
+ // a single interstitial is shown for the second URL.
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb;
+ GURL phishing_url("http://phishingurl.com/");
+
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableClientSidePhishingInterstitial);
+
+ EXPECT_CALL(*csd_service_,
+ SendClientReportPhishingRequest(phishing_url, 1.0, _))
+ .WillOnce(SaveArg<2>(&cb));
+ OnDetectedPhishingSite(phishing_url, 1.0);
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+ ASSERT_TRUE(cb != NULL);
+ GURL other_phishing_url("http://other_phishing_url.com/bla");
+ // We navigate away. The callback cb should be revoked.
+ NavigateAndCommit(other_phishing_url);
+
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb_other;
+ EXPECT_CALL(*csd_service_,
+ SendClientReportPhishingRequest(other_phishing_url, 0.8, _))
+ .WillOnce(SaveArg<2>(&cb_other));
+ OnDetectedPhishingSite(other_phishing_url, 0.8);
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get()));
+ ASSERT_TRUE(cb_other);
+
+ // We expect that the interstitial is shown for the second phishing URL and
+ // not for the first phishing URL.
+ EXPECT_CALL(*sb_service_,
+ DisplayBlockingPage(phishing_url, phishing_url,_, _, _, _, _, _))
+ .Times(0);
+ SafeBrowsingService::Client* client;
+ EXPECT_CALL(*sb_service_,
+ DisplayBlockingPage(
+ other_phishing_url,
+ other_phishing_url,
+ _,
+ ResourceType::MAIN_FRAME,
+ SafeBrowsingService::URL_PHISHING,
+ _ /* a CsdClient object */,
+ contents()->GetRenderProcessHost()->id(),
+ contents()->render_view_host()->routing_id()))
+ .WillOnce(SaveArg<5>(&client));
+
+ cb->Run(phishing_url, true); // Should have no effect.
+ delete cb;
+ cb_other->Run(other_phishing_url, true); // Should show interstitial.
+ delete cb_other;
+
+ FlushIOMessageLoop();
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get()));
+
+ // Make sure the client object will be deleted.
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(
+ sb_service_.get(),
+ &MockSafeBrowsingService::InvokeOnBlockingPageComplete,
+ client));
+ // Since the CsdClient object will be deleted on the UI thread I need
+ // to run the UI message loop. Post a task to stop the UI message loop
+ // after the client object destructor is called.
+ FlushIOMessageLoop();
+}
+
+} // 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 6a0a99e..48bf42e 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_service.cc
@@ -445,7 +445,8 @@ void ClientSideDetectionService::HandlePhishingVerdict(
info->callback->Run(info->phishing_url, response.phishy());
} else {
DLOG(ERROR) << "Unable to get the server verdict for URL: "
- << info->phishing_url;
+ << info->phishing_url << " status: " << status.status() << " "
+ << "response_code:" << response_code;
info->callback->Run(info->phishing_url, false);
}
client_phishing_reports_.erase(source);
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.h b/chrome/browser/safe_browsing/client_side_detection_service.h
index fcc823d..2c37202 100644
--- a/chrome/browser/safe_browsing/client_side_detection_service.h
+++ b/chrome/browser/safe_browsing/client_side_detection_service.h
@@ -91,11 +91,16 @@ class ClientSideDetectionService : public URLFetcher::Delegate,
// fetch. If an error occurs the phishing verdict will always be false. The
// callback is always called after SendClientReportPhishingRequest() returns
// and on the same thread as SendClientReportPhishingRequest() was called.
- void SendClientReportPhishingRequest(
+ virtual void SendClientReportPhishingRequest(
const GURL& phishing_url,
double score,
ClientReportPhishingRequestCallback* callback);
+ protected:
+ // Use Create() method to create an instance of this object.
+ ClientSideDetectionService(const FilePath& model_path,
+ URLRequestContextGetter* request_context_getter);
+
private:
friend class ClientSideDetectionServiceTest;
friend class ClientSideDetectionServiceHooksTest;
@@ -127,10 +132,6 @@ class ClientSideDetectionService : public URLFetcher::Delegate,
static const base::TimeDelta kNegativeCacheInterval;
static const base::TimeDelta kPositiveCacheInterval;
- // Use Create() method to create an instance of this object.
- ClientSideDetectionService(const FilePath& model_path,
- URLRequestContextGetter* request_context_getter);
-
// Sets the model status and invokes all the pending callbacks in
// |open_callbacks_| with the current |model_file_| as parameter.
void SetModelStatus(ModelStatus status);
diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h
index aa095da..396df13 100644
--- a/chrome/browser/safe_browsing/safe_browsing_service.h
+++ b/chrome/browser/safe_browsing/safe_browsing_service.h
@@ -160,14 +160,14 @@ class SafeBrowsingService
// If the request contained a chain of redirects, |url| is the last url
// in the chain, and |original_url| is the first one (the root of the
// chain). Otherwise, |original_url| = |url|.
- void DisplayBlockingPage(const GURL& url,
- const GURL& original_url,
- const std::vector<GURL>& redirect_urls,
- ResourceType::Type resource_type,
- UrlCheckResult result,
- Client* client,
- int render_process_host_id,
- int render_view_id);
+ virtual void DisplayBlockingPage(const GURL& url,
+ const GURL& original_url,
+ const std::vector<GURL>& redirect_urls,
+ ResourceType::Type resource_type,
+ UrlCheckResult result,
+ Client* client,
+ int render_process_host_id,
+ int render_view_id);
// Called on the IO thread when the SafeBrowsingProtocolManager has received
// the full hash results for prefix hits detected in the database.
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 223668c..55d8c83 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -64,6 +64,7 @@
#include "chrome/browser/renderer_host/site_instance.h"
#include "chrome/browser/renderer_host/web_cache_manager.h"
#include "chrome/browser/renderer_preferences_util.h"
+#include "chrome/browser/safe_browsing/client_side_detection_host.h"
#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/tab_contents/infobar_delegate.h"
#include "chrome/browser/tab_contents/interstitial_page.h"
@@ -412,6 +413,8 @@ void TabContents::AddObservers() {
desktop_notification_handler_.reset(
new DesktopNotificationHandlerForTC(this, GetRenderProcessHost()));
plugin_observer_.reset(new PluginObserver(this));
+ safebrowsing_detection_host_.reset(new safe_browsing::ClientSideDetectionHost(
+ this));
}
// static
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index 87ae5ab..9c40213 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -59,6 +59,10 @@ class PrintPreviewMessageHandler;
class PrintViewManager;
}
+namespace safe_browsing {
+class ClientSideDetectionHost;
+}
+
class AutocompleteHistoryManager;
class AutoFillManager;
class BlockedContentContainer;
@@ -710,6 +714,10 @@ class TabContents : public PageNavigator,
}
AutoFillManager* autofill_manager() { return autofill_manager_.get(); }
+ safe_browsing::ClientSideDetectionHost* safebrowsing_detection_host() {
+ return safebrowsing_detection_host_.get();
+ }
+
protected:
// from RenderViewHostDelegate.
virtual bool OnMessageReceived(const IPC::Message& message);
@@ -1093,6 +1101,9 @@ class TabContents : public PageNavigator,
// Handles desktop notification IPCs.
scoped_ptr<DesktopNotificationHandlerForTC> desktop_notification_handler_;
+ // Handles IPCs related to SafeBrowsing client-side phishing detection.
+ scoped_ptr<safe_browsing::ClientSideDetectionHost>
+ safebrowsing_detection_host_;
// Data for loading state ----------------------------------------------------
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 796cf5b..767c16c 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2044,6 +2044,8 @@
'browser/safe_browsing/bloom_filter.h',
'browser/safe_browsing/chunk_range.cc',
'browser/safe_browsing/chunk_range.h',
+ 'browser/safe_browsing/client_side_detection_host.cc',
+ 'browser/safe_browsing/client_side_detection_host.h',
'browser/safe_browsing/client_side_detection_service.cc',
'browser/safe_browsing/client_side_detection_service.h',
'<(protoc_out_dir)/chrome/browser/safe_browsing/csd.pb.cc',
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi
index 1ee077a..2c4025b 100644
--- a/chrome/chrome_common.gypi
+++ b/chrome/chrome_common.gypi
@@ -151,6 +151,8 @@
'common/resource_response.cc',
'common/resource_response.h',
'common/result_codes.h',
+ 'common/safebrowsing_messages.cc',
+ 'common/safebrowsing_messages.h',
'common/sandbox_init_wrapper.h',
'common/sandbox_init_wrapper_linux.cc',
'common/sandbox_init_wrapper_mac.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index fceeafb..91bc8cf 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1462,6 +1462,7 @@
'browser/rlz/rlz_unittest.cc',
'browser/safe_browsing/bloom_filter_unittest.cc',
'browser/safe_browsing/chunk_range_unittest.cc',
+ 'browser/safe_browsing/client_side_detection_host_unittest.cc',
'browser/safe_browsing/client_side_detection_service_unittest.cc',
'browser/safe_browsing/malware_details_unittest.cc',
'browser/safe_browsing/prefix_set_unittest.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index a80cae4..1d6a88b 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -457,6 +457,13 @@ const char kEnableBenchmarking[] = "enable-benchmarking";
const char kEnableClientSidePhishingDetection[] =
"enable-client-side-phishing-detection";
+// At this point, even if client-side phishing detection is enabled we will not,
+// by default, display an interstitial if we detected a phishing site. Once
+// we are confident that the false-positive rate is as low as expected we can
+// remove this flag.
+const char kEnableClientSidePhishingInterstitial[] =
+ "enable-client-side-phishing-interstitial";
+
// This flag enables UI for clearing server data. Temporarily in place
// until there's a server endpoint deployed.
const char kEnableClearServerData[] = "enable-clear-server-data";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index c11445e..7b654ae 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -137,6 +137,7 @@ extern const char kEnableAeroPeekTabs[];
extern const char kEnableAuthNegotiatePort[];
extern const char kEnableBenchmarking[];
extern const char kEnableClientSidePhishingDetection[];
+extern const char kEnableClientSidePhishingInterstitial[];
extern const char kEnableClearServerData[];
extern const char kEnableClickToPlay[];
extern const char kEnableCloudPrintProxy[];
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index e304f4e..56f1877 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -2511,13 +2511,6 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_InstantSupportDetermined,
int32 /* page_id */,
bool /* result */)
-// Client-Side Phishing Detector ---------------------------------------------
-// Inform the browser that the current URL is phishing according to the
-// client-side phishing detector.
-IPC_MESSAGE_ROUTED2(ViewHostMsg_DetectedPhishingSite,
- GURL /* phishing_url */,
- double /* phishing_score */)
-
// Response from ViewMsg_ScriptEvalRequest. The ID is the parameter supplied
// to ViewMsg_ScriptEvalRequest. The result has the value returned by the
// script as it's only element, one of Null, Boolean, Integer, Real, Date, or
diff --git a/chrome/common/safebrowsing_messages.cc b/chrome/common/safebrowsing_messages.cc
new file mode 100644
index 0000000..b10de4b
--- /dev/null
+++ b/chrome/common/safebrowsing_messages.cc
@@ -0,0 +1,8 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/common_param_traits.h"
+
+#define IPC_MESSAGE_IMPL
+#include "chrome/common/safebrowsing_messages.h"
diff --git a/chrome/common/safebrowsing_messages.h b/chrome/common/safebrowsing_messages.h
new file mode 100644
index 0000000..072d3c6
--- /dev/null
+++ b/chrome/common/safebrowsing_messages.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_SAFEBROWSING_MESSAGES_H_
+#define CHROME_COMMON_SAFEBROWSING_MESSAGES_H_
+#pragma once
+
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_message_macros.h"
+
+#define IPC_MESSAGE_START SafeBrowsingMsgStart
+
+// SafeBrowsing client-side detection messages sent from the renderer to the
+// browser.
+
+// Inform the browser that the current URL is phishing according to the
+// client-side phishing detector.
+IPC_MESSAGE_ROUTED2(SafeBrowsingDetectionHostMsg_DetectedPhishingSite,
+ GURL /* phishing_url */,
+ double /* phishing_score */)
+
+// SafeBrowsing client-side detection messages sent from the browser to the
+// renderer. TODO(noelutz): move other IPC messages here.
+
+#endif // CHROME_COMMON_SAFEBROWSING_MESSAGES_H_
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
index 985965d..1c3dbd7 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/scoped_callback_factory.h"
#include "chrome/common/render_messages.h"
+#include "chrome/common/safebrowsing_messages.h"
#include "chrome/renderer/navigation_state.h"
#include "chrome/renderer/render_thread.h"
#include "chrome/renderer/render_view.h"
@@ -166,8 +167,8 @@ void PhishingClassifierDelegate::ClassificationDone(bool is_phishy,
return;
}
- render_view()->Send(new ViewHostMsg_DetectedPhishingSite(
- render_view()->routing_id(),
+ Send(new SafeBrowsingDetectionHostMsg_DetectedPhishingSite(
+ routing_id(),
last_url_sent_to_classifier_,
phishy_score));
}
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
index 8131bf8..cceb2a3 100644
--- a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
@@ -9,7 +9,7 @@
#include "base/scoped_ptr.h"
#include "base/utf_string_conversions.h"
-#include "chrome/common/render_messages.h"
+#include "chrome/common/safebrowsing_messages.h"
#include "chrome/renderer/render_view.h"
#include "chrome/renderer/safe_browsing/features.h"
#include "chrome/renderer/safe_browsing/phishing_classifier.h"
@@ -61,8 +61,8 @@ class PhishingClassifierDelegateTest : public RenderViewFakeResourcesTest {
bool OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PhishingClassifierDelegateTest, message)
- IPC_MESSAGE_HANDLER(ViewHostMsg_DetectedPhishingSite,
- OnDetectedPhishingSite)
+ IPC_MESSAGE_HANDLER(SafeBrowsingDetectionHostMsg_DetectedPhishingSite,
+ OnDetectedPhishingSite)
IPC_MESSAGE_UNHANDLED(
handled = RenderViewFakeResourcesTest::OnMessageReceived(message))
IPC_END_MESSAGE_MAP()
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 75231d8..17941aa0 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -68,6 +68,7 @@ enum IPCMessageStart {
SpeechInputMsgStart,
PepperMsgStart,
AutoFillMsgStart,
+ SafeBrowsingMsgStart,
};
class DictionaryValue;