summaryrefslogtreecommitdiffstats
path: root/chrome/browser/safe_browsing
diff options
context:
space:
mode:
authorbryner@chromium.org <bryner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-11 23:54:16 +0000
committerbryner@chromium.org <bryner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-11 23:54:16 +0000
commitb201c43673a3cf10e10004b06f0c434c88fdd6d5 (patch)
treefcd82305e73934a7906db60a7bb20b4bc8ba20c1 /chrome/browser/safe_browsing
parentd6c997fe9d62350b0923a4b2cb3838db1b049b26 (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service.cc104
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service.h15
-rw-r--r--chrome/browser/safe_browsing/client_side_detection_service_unittest.cc79
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