Bug 1254310 - Add a hidden pref to temporarily disable Safe Browsing on given hostnames. r=gcp
authorFrancois Marier <francois@mozilla.com>
Fri, 11 Mar 2016 13:57:38 -0800
changeset 288362 abcea78b37db62ac097e24ba3d2156789b14b5ec
parent 288361 6e8cd31f8397ae2458fdccfc18397145dce64526
child 288363 2fa9f00493a90f8e34cbcdd68bb760666c592aa9
push id30079
push userryanvm@gmail.com
push dateSat, 12 Mar 2016 20:24:19 +0000
treeherdermozilla-central@d1d47ba19ce9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp
bugs1254310
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1254310 - Add a hidden pref to temporarily disable Safe Browsing on given hostnames. r=gcp
browser/components/safebrowsing/content/test/browser.ini
browser/components/safebrowsing/content/test/browser_whitelisted.js
netwerk/base/nsChannelClassifier.cpp
netwerk/base/nsChannelClassifier.h
--- a/browser/components/safebrowsing/content/test/browser.ini
+++ b/browser/components/safebrowsing/content/test/browser.ini
@@ -2,8 +2,9 @@
 support-files = head.js
 
 [browser_forbidden.js]
 [browser_bug400731.js]
 [browser_bug415846.js]
 skip-if = os == "mac" || e10s
 # Disabled on Mac because of its bizarre special-and-unique
 # snowflake of a help menu.
+[browser_whitelisted.js]
copy from browser/components/safebrowsing/content/test/browser_forbidden.js
copy to browser/components/safebrowsing/content/test/browser_whitelisted.js
--- a/browser/components/safebrowsing/content/test/browser_forbidden.js
+++ b/browser/components/safebrowsing/content/test/browser_whitelisted.js
@@ -1,40 +1,41 @@
-/* Ensure that pages in the forbidden list are blocked. */
+/* Ensure that hostnames in the whitelisted pref are not blocked. */
 
-const PREF_FORBIDDEN_ENABLED = "browser.safebrowsing.forbiddenURIs.enabled";
-const BENIGN_PAGE = "http://example.com/";
-const FORBIDDEN_PAGE = "http://www.itisatrap.org/firefox/forbidden.html";
+const PREF_WHITELISTED_HOSTNAMES = "urlclassifier.skipHostnames";
+const TEST_PAGE = "http://www.itisatrap.org/firefox/its-an-attack.html";
 var tabbrowser = null;
 
 registerCleanupFunction(function() {
   tabbrowser = null;
-  Services.prefs.clearUserPref(PREF_FORBIDDEN_ENABLED);
+  Services.prefs.clearUserPref(PREF_WHITELISTED_HOSTNAMES);
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 });
 
-function testBenignPage(window) {
-  info("Non-forbidden content must not be blocked");
+function testBlockedPage(window) {
+  info("Non-whitelisted pages must be blocked");
+  ok(true, "about:blocked was shown");
+}
+
+function testWhitelistedPage(window) {
+  info("Whitelisted pages must be skipped");
   var getmeout_button = window.document.getElementById("getMeOutButton");
   var ignorewarning_button = window.document.getElementById("ignoreWarningButton");
   ok(!getmeout_button, "GetMeOut button not present");
   ok(!ignorewarning_button, "IgnoreWarning button not present");
 }
 
-function testForbiddenPage(window) {
-  info("Forbidden content must be blocked");
-  ok(true, "about:blocked was shown");
-}
-
 add_task(function* testNormalBrowsing() {
   tabbrowser = gBrowser;
   let tab = tabbrowser.selectedTab = tabbrowser.addTab();
 
-  info("Load a test page that's not forbidden");
-  yield promiseTabLoadEvent(tab, BENIGN_PAGE, "load");
-  testBenignPage(tab.ownerDocument.defaultView);
+  info("Load a test page that's whitelisted");
+  Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "example.com,www.ItIsaTrap.org,example.net");
+  yield promiseTabLoadEvent(tab, TEST_PAGE, "load");
+  testWhitelistedPage(tab.ownerDocument.defaultView);
 
-  info("Load a test page that is forbidden");
-  yield promiseTabLoadEvent(tab, FORBIDDEN_PAGE, "AboutBlockedLoaded");
-  testForbiddenPage(tab.ownerDocument.defaultView);
+  info("Load a test page that's no longer whitelisted");
+  Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "");
+  yield promiseTabLoadEvent(tab, TEST_PAGE, "AboutBlockedLoaded");
+  testBlockedPage(tab.ownerDocument.defaultView);
 });
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -2,16 +2,17 @@
 /* vim: set sw=2 sts=2 ts=8 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsChannelClassifier.h"
 
 #include "mozIThirdPartyUtil.h"
+#include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsICacheEntry.h"
 #include "nsICachingChannel.h"
 #include "nsIChannel.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIHttpChannelInternal.h"
@@ -308,16 +309,28 @@ nsChannelClassifier::StartInternal()
     if (hasFlags) return NS_ERROR_UNEXPECTED;
 
     rv = NS_URIChainHasFlags(uri,
                              nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
                              &hasFlags);
     NS_ENSURE_SUCCESS(rv, rv);
     if (hasFlags) return NS_ERROR_UNEXPECTED;
 
+    // Skip whitelisted hostnames.
+    nsAutoCString whitelisted;
+    Preferences::GetCString("urlclassifier.skipHostnames", &whitelisted);
+    if (!whitelisted.IsEmpty()) {
+      ToLowerCase(whitelisted);
+      LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
+           this, whitelisted.get()));
+      if (IsHostnameWhitelisted(uri, whitelisted)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+    }
+
     nsCOMPtr<nsIURIClassifier> uriClassifier =
         do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
     if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
         rv == NS_ERROR_NOT_AVAILABLE) {
         // no URI classifier, ignore this failure.
         return NS_ERROR_NOT_AVAILABLE;
     }
     NS_ENSURE_SUCCESS(rv, rv);
@@ -367,16 +380,40 @@ nsChannelClassifier::StartInternal()
     } else {
         LOG(("nsChannelClassifier[%p]: not expecting callback", this));
         return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
 }
 
+bool
+nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri,
+                                           const nsACString &aWhitelisted)
+{
+  nsAutoCString host;
+  nsresult rv = aUri->GetHost(host);
+  if (NS_FAILED(rv) || host.IsEmpty()) {
+    return false;
+  }
+  ToLowerCase(host);
+
+  nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ',');
+  while (tokenizer.hasMoreTokens()) {
+    const nsCSubstring& token = tokenizer.nextToken();
+    if (token.Equals(host)) {
+      LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
+           this, host.get()));
+      return true;
+    }
+  }
+
+  return false;
+}
+
 // Note in the cache entry that this URL was classified, so that future
 // cached loads don't need to be checked.
 void
 nsChannelClassifier::MarkEntryClassified(nsresult status)
 {
     // Should only be called in the parent process.
     MOZ_ASSERT(XRE_IsParentProcess());
 
--- a/netwerk/base/nsChannelClassifier.h
+++ b/netwerk/base/nsChannelClassifier.h
@@ -39,16 +39,18 @@ private:
     void MarkEntryClassified(nsresult status);
     bool HasBeenClassified(nsIChannel *aChannel);
     // Helper function so that we ensure we call ContinueBeginConnect once
     // Start is called. Returns NS_OK if and only if we will get a callback
     // from the classifier service.
     nsresult StartInternal();
     // Helper function to check a tracking URI against the whitelist
     nsresult IsTrackerWhitelisted();
+    // Helper function to check a URI against the hostname whitelist
+    bool IsHostnameWhitelisted(nsIURI *aUri, const nsACString &aWhitelisted);
     // Checks that the channel was loaded by the URI currently loaded in aDoc
     static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
 
 public:
     // If we are blocking tracking content, update the corresponding flag in
     // the respective docshell and call nsISecurityEventSink::onSecurityChange.
     static nsresult SetBlockedTrackingContent(nsIChannel *channel);
     static nsresult NotifyTrackingProtectionDisabled(nsIChannel *aChannel);