diff options
author | bryner@chromium.org <bryner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-11 23:54:16 +0000 |
---|---|---|
committer | bryner@chromium.org <bryner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-11 23:54:16 +0000 |
commit | b201c43673a3cf10e10004b06f0c434c88fdd6d5 (patch) | |
tree | fcd82305e73934a7906db60a7bb20b4bc8ba20c1 /chrome/browser/safe_browsing | |
parent | d6c997fe9d62350b0923a4b2cb3838db1b049b26 (diff) | |
download | chromium_src-b201c43673a3cf10e10004b06f0c434c88fdd6d5.zip chromium_src-b201c43673a3cf10e10004b06f0c434c88fdd6d5.tar.gz chromium_src-b201c43673a3cf10e10004b06f0c434c88fdd6d5.tar.bz2 |
Run pre-classification checks in the browser before starting client-side phishing detection.
This CL just adds a framework for running these checks, and adjusts the
renderer-side logic to delay phishing classification until the browser has
sent an IPC signaling that it should run. Future CL's will add the actual
checks, which will include a whitelist lookup, intranet hostname checks, and
per-day ping limit.
BUG=none
TEST=updated existing tests
Review URL: http://codereview.chromium.org/6398001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74698 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/safe_browsing')
3 files changed, 197 insertions, 1 deletions
diff --git a/chrome/browser/safe_browsing/client_side_detection_service.cc b/chrome/browser/safe_browsing/client_side_detection_service.cc index 3d55132..9d2bb07 100644 --- a/chrome/browser/safe_browsing/client_side_detection_service.cc +++ b/chrome/browser/safe_browsing/client_side_detection_service.cc @@ -16,10 +16,16 @@ #include "base/task.h" #include "base/time.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/safe_browsing/csd.pb.h" +#include "chrome/browser/tab_contents/provisional_load_details.h" +#include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/net/http_return.h" #include "chrome/common/net/url_fetcher.h" #include "chrome/common/net/url_request_context_getter.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/render_messages.h" #include "googleurl/src/gurl.h" #include "net/base/load_flags.h" #include "net/url_request/url_request_status.h" @@ -38,6 +44,73 @@ struct ClientSideDetectionService::ClientReportInfo { GURL phishing_url; }; +// ShouldClassifyUrlRequest tracks the pre-classification checks for a +// toplevel URL that has started loading into a renderer. When these +// checks are complete, the renderer is notified if it should run +// client-side phishing classification, then the ShouldClassifyUrlRequest +// deletes itself. +class ClientSideDetectionService::ShouldClassifyUrlRequest + : public NotificationObserver { + public: + ShouldClassifyUrlRequest(const GURL& url, TabContents* tab_contents) + : url_(url), + tab_contents_(tab_contents), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + registrar_.Add(this, + NotificationType::TAB_CONTENTS_DESTROYED, + Source<TabContents>(tab_contents)); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::TAB_CONTENTS_DESTROYED: + Cancel(); + break; + default: + NOTREACHED(); + }; + } + + void Start() { + // TODO(bryner): add pre-classification checks here. + // For now we just call Finish() asynchronously for consistency, + // since the pre-classification checks are asynchronous. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + method_factory_.NewRunnableMethod( + &ShouldClassifyUrlRequest::Finish)); + } + + private: + // This object always deletes itself, so make the destructor private. + virtual ~ShouldClassifyUrlRequest() {} + + void Cancel() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + tab_contents_ = NULL; + Finish(); + } + + void Finish() { + if (tab_contents_) { + RenderViewHost* rvh = tab_contents_->render_view_host(); + rvh->Send(new ViewMsg_StartPhishingDetection(rvh->routing_id(), url_)); + } + delete this; + } + + GURL url_; + TabContents* tab_contents_; + NotificationRegistrar registrar_; + ScopedRunnableMethodFactory<ShouldClassifyUrlRequest> method_factory_; + + DISALLOW_COPY_AND_ASSIGN(ShouldClassifyUrlRequest); +}; + ClientSideDetectionService::ClientSideDetectionService( const FilePath& model_path, URLRequestContextGetter* request_context_getter) @@ -47,6 +120,11 @@ ClientSideDetectionService::ClientSideDetectionService( ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(callback_factory_(this)), request_context_getter_(request_context_getter) { + // Register to find out when pages begin loading into a renderer. + // When this happens, we'll start our pre-classificaton checks. + registrar_.Add(this, + NotificationType::FRAME_PROVISIONAL_LOAD_COMMITTED, + NotificationService::AllSources()); } ClientSideDetectionService::~ClientSideDetectionService() { @@ -118,6 +196,32 @@ void ClientSideDetectionService::OnURLFetchComplete( } } +void ClientSideDetectionService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::FRAME_PROVISIONAL_LOAD_COMMITTED: { + // Check whether the load should trigger a phishing classification. + // This is true if the navigation happened in the main frame and was + // not an in-page navigation. + ProvisionalLoadDetails* load_details = + Details<ProvisionalLoadDetails>(details).ptr(); + + if (load_details->main_frame() && !load_details->in_page_navigation()) { + NavigationController* controller = + Source<NavigationController>(source).ptr(); + ShouldClassifyUrlRequest* request = + new ShouldClassifyUrlRequest(load_details->url(), + controller->tab_contents()); + request->Start(); // the request will delete itself on completion + } + break; + } + default: + NOTREACHED(); + }; +} + void ClientSideDetectionService::SetModelStatus(ModelStatus status) { DCHECK_NE(READY_STATUS, model_status_); model_status_ = status; diff --git a/chrome/browser/safe_browsing/client_side_detection_service.h b/chrome/browser/safe_browsing/client_side_detection_service.h index a9bc7f6..c299ca6 100644 --- a/chrome/browser/safe_browsing/client_side_detection_service.h +++ b/chrome/browser/safe_browsing/client_side_detection_service.h @@ -33,6 +33,8 @@ #include "base/time.h" #include "chrome/browser/safe_browsing/csd.pb.h" #include "chrome/common/net/url_fetcher.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" #include "googleurl/src/gurl.h" class URLRequestContextGetter; @@ -43,7 +45,8 @@ class URLRequestStatus; namespace safe_browsing { -class ClientSideDetectionService : public URLFetcher::Delegate { +class ClientSideDetectionService : public URLFetcher::Delegate, + public NotificationObserver { public: typedef Callback1<base::PlatformFile>::Type OpenModelDoneCallback; @@ -67,6 +70,11 @@ class ClientSideDetectionService : public URLFetcher::Delegate { const ResponseCookies& cookies, const std::string& data); + // From the NotificationObserver interface. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + // Gets the model file descriptor once the model is ready and stored // on disk. If there was an error the callback is called and the // platform file is set to kInvalidPlatformFileValue. The @@ -89,6 +97,8 @@ class ClientSideDetectionService : public URLFetcher::Delegate { private: friend class ClientSideDetectionServiceTest; + friend class ClientSideDetectionServiceHooksTest; + class ShouldClassifyUrlRequest; enum ModelStatus { // It's unclear whether or not the model was already fetched. @@ -199,6 +209,9 @@ class ClientSideDetectionService : public URLFetcher::Delegate { // The context we use to issue network requests. scoped_refptr<URLRequestContextGetter> request_context_getter_; + // Used to register for page load notifications. + NotificationRegistrar registrar_; + DISALLOW_COPY_AND_ASSIGN(ClientSideDetectionService); }; 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 b418d14..b87758c 100644 --- a/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc +++ b/chrome/browser/safe_browsing/client_side_detection_service_unittest.cc @@ -19,11 +19,15 @@ #include "base/time.h" #include "testing/gtest/include/gtest/gtest.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_service.h" #include "chrome/browser/safe_browsing/csd.pb.h" +#include "chrome/common/render_messages.h" #include "chrome/common/net/test_url_fetcher_factory.h" #include "chrome/common/net/url_fetcher.h" #include "googleurl/src/gurl.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_test_sink.h" #include "net/url_request/url_request_status.h" namespace safe_browsing { @@ -224,4 +228,79 @@ TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) { EXPECT_EQ(2, GetNumReportsPerDay()); } +// We use a separate test fixture for testing the ClientSideDetectionService's +// handling of load notifications from TabContents. This uses +// RenderViewHostTestHarness to set up a fake TabContents and related objects. +class ClientSideDetectionServiceHooksTest : public RenderViewHostTestHarness, + public IPC::Channel::Listener { + public: + // IPC::Channel::Listener + virtual bool OnMessageReceived(const IPC::Message& msg) { + if (msg.type() == ViewMsg_StartPhishingDetection::ID) { + received_msg_ = msg; + did_receive_msg_ = true; + return true; + } + return false; + } + + protected: + virtual void SetUp() { + RenderViewHostTestHarness::SetUp(); + file_thread_.reset(new BrowserThread(BrowserThread::FILE, &message_loop_)); + ui_thread_.reset(new BrowserThread(BrowserThread::UI, &message_loop_)); + + // We're not exercising model fetching here, so just set up a canned + // success response. + factory_.reset(new FakeURLFetcherFactory()); + factory_->SetFakeResponse(ClientSideDetectionService::kClientModelUrl, + "dummy model data", true); + URLFetcher::set_factory(factory_.get()); + + process()->sink().AddFilter(this); + } + + virtual void TearDown() { + process()->sink().RemoveFilter(this); + URLFetcher::set_factory(NULL); + file_thread_.reset(); + ui_thread_.reset(); + RenderViewHostTestHarness::TearDown(); + } + + scoped_ptr<FakeURLFetcherFactory> factory_; + scoped_ptr<BrowserThread> ui_thread_; + scoped_ptr<BrowserThread> file_thread_; + IPC::Message received_msg_; + bool did_receive_msg_; +}; + +TEST_F(ClientSideDetectionServiceHooksTest, ShouldClassifyUrl) { + ScopedTempDir tmp_dir; + ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); + FilePath model_path = tmp_dir.path().AppendASCII("model"); + + scoped_ptr<ClientSideDetectionService> csd_service( + ClientSideDetectionService::Create(model_path, NULL)); + + // Navigate the tab to a page. We should see a StartPhishingDetection IPC. + did_receive_msg_ = false; + NavigateAndCommit(GURL("http://host.com/")); + // The IPC is sent asynchronously, so run the message loop to wait for + // the message. + MessageLoop::current()->RunAllPending(); + ASSERT_TRUE(did_receive_msg_); + + Tuple1<GURL> url; + ViewMsg_StartPhishingDetection::Read(&received_msg_, &url); + EXPECT_EQ(GURL("http://host.com/"), url.a); + EXPECT_EQ(rvh()->routing_id(), received_msg_.routing_id()); + + // Now try an in-page navigation. This should not trigger an IPC. + did_receive_msg_ = false; + NavigateAndCommit(GURL("http://host.com/#foo")); + MessageLoop::current()->RunAllPending(); + ASSERT_FALSE(did_receive_msg_); +} + } // namespace safe_browsing |