diff options
author | noelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-17 19:47:13 +0000 |
---|---|---|
committer | noelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-17 19:47:13 +0000 |
commit | fa685ffdc706738091a9243e12da2c89f2a01f0f (patch) | |
tree | e9635360daabc26604bbf842f9873c0ce12c19ac | |
parent | bd6e45ffd4f1c0e805e76a472b50f8f05e3e82e0 (diff) | |
download | chromium_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
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; |