Bug 1254310 - Add a hidden pref to temporarily disable Safe Browsing on given hostnames. r=gcp a=ritu
authorFrancois Marier <francois@mozilla.com>
Fri, 11 Mar 2016 13:57:38 -0800
changeset 325634 9f8275d6c8221609174c85dbef649dda82dc9272
parent 325633 9985cfb9f7aee2fecbe79273afab51649435e8b8
child 325635 e50bd4f62a1f8a757acade4882617a3fa4547b64
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp, ritu
bugs1254310
milestone47.0a2
Bug 1254310 - Add a hidden pref to temporarily disable Safe Browsing on given hostnames. r=gcp a=ritu MozReview-Commit-ID: BaA3orXyLu9
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);