Bug 1307596 - Add a preference for trimming third-party referrers. r?dragana draft
authorFrancois Marier <francois@mozilla.com>
Wed, 05 Oct 2016 16:36:16 -0700
changeset 424525 d802ca97aef4c487109e78adf41ffc35dbefb8f2
parent 422670 a835589ae0c63a2d91be150d80b5fc600e44b447
child 533698 6038d36e42e0758ae63656ea9f9d4d7be00e1134
push id32179
push userfmarier@mozilla.com
push dateWed, 12 Oct 2016 22:04:02 +0000
reviewersdragana
bugs1307596
milestone52.0a1
Bug 1307596 - Add a preference for trimming third-party referrers. r?dragana MozReview-Commit-ID: EL2L4yMnwAi
modules/libpref/init/all.js
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
netwerk/test/unit/test_referrer.js
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1448,16 +1448,18 @@ pref("network.http.accept.default", "tex
 
 // Prefs allowing granular control of referers
 // 0=don't send any, 1=send only on clicks, 2=send on image requests as well
 pref("network.http.sendRefererHeader",      2);
 // false=real referer, true=spoof referer (use target URI as referer)
 pref("network.http.referer.spoofSource", false);
 // 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
 pref("network.http.referer.trimmingPolicy", 0);
+// 0=full URI, 1=scheme+host+port+path, 2=scheme+host+port
+pref("network.http.referer.XOriginTrimmingPolicy", 0);
 // 0=always send, 1=send iff base domains match, 2=send iff hosts match
 pref("network.http.referer.XOriginPolicy", 0);
 
 // Controls whether we send HTTPS referres to other HTTPS sites.
 // By default this is enabled for compatibility (see bug 141641)
 pref("network.http.sendSecureXSiteReferrer", true);
 
 // Controls whether referrer attributes in <a>, <img>, <area>, <iframe>, and <link> are honoured
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1512,16 +1512,25 @@ HttpBaseChannel::SetReferrerWithPolicy(n
 
   // strip away any userpass; we don't want to be giving out passwords ;-)
   // This is required by Referrer Policy stripping algorithm.
   rv = clone->SetUserPass(EmptyCString());
   if (NS_FAILED(rv)) return rv;
 
   nsAutoCString spec;
 
+  // Apply the user cross-origin trimming policy if it's more
+  // restrictive than the general one.
+  if (isCrossOrigin) {
+    int userReferrerXOriginTrimmingPolicy =
+      gHttpHandler->ReferrerXOriginTrimmingPolicy();
+    userReferrerTrimmingPolicy =
+      std::max(userReferrerTrimmingPolicy, userReferrerXOriginTrimmingPolicy);
+  }
+
   // site-specified referrer trimming may affect the trim level
   // "unsafe-url" behaves like "origin" (send referrer in the same situations) but
   // "unsafe-url" sends the whole referrer and origin removes the path.
   // "origin-when-cross-origin" trims the referrer only when the request is
   // cross-origin.
   // "Strict" request from https->http case was bailed out, so here:
   // "strict-origin" behaves the same as "origin".
   // "strict-origin-when-cross-origin" behaves the same as "origin-when-cross-origin"
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -161,16 +161,17 @@ nsHttpHandler *gHttpHandler = nullptr;
 
 nsHttpHandler::nsHttpHandler()
     : mHttpVersion(NS_HTTP_VERSION_1_1)
     , mProxyHttpVersion(NS_HTTP_VERSION_1_1)
     , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
     , mReferrerLevel(0xff) // by default we always send a referrer
     , mSpoofReferrerSource(false)
     , mReferrerTrimmingPolicy(0)
+    , mReferrerXOriginTrimmingPolicy(0)
     , mReferrerXOriginPolicy(0)
     , mFastFallbackToIPv4(false)
     , mProxyPipelining(true)
     , mIdleTimeout(PR_SecondsToInterval(10))
     , mSpdyTimeout(PR_SecondsToInterval(180))
     , mResponseTimeout(PR_SecondsToInterval(300))
     , mResponseTimeoutEnabled(false)
     , mNetworkChangedTimeout(5000)
@@ -1080,16 +1081,22 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
     }
 
     if (PREF_CHANGED(HTTP_PREF("referer.trimmingPolicy"))) {
         rv = prefs->GetIntPref(HTTP_PREF("referer.trimmingPolicy"), &val);
         if (NS_SUCCEEDED(rv))
             mReferrerTrimmingPolicy = (uint8_t) clamped(val, 0, 2);
     }
 
+    if (PREF_CHANGED(HTTP_PREF("referer.XOriginTrimmingPolicy"))) {
+        rv = prefs->GetIntPref(HTTP_PREF("referer.XOriginTrimmingPolicy"), &val);
+        if (NS_SUCCEEDED(rv))
+            mReferrerXOriginTrimmingPolicy = (uint8_t) clamped(val, 0, 2);
+    }
+
     if (PREF_CHANGED(HTTP_PREF("referer.XOriginPolicy"))) {
         rv = prefs->GetIntPref(HTTP_PREF("referer.XOriginPolicy"), &val);
         if (NS_SUCCEEDED(rv))
             mReferrerXOriginPolicy = (uint8_t) clamped(val, 0, 0xff);
     }
 
     if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) {
         rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val);
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -77,16 +77,19 @@ public:
 
     const nsAFlatCString &UserAgent();
 
     nsHttpVersion  HttpVersion()             { return mHttpVersion; }
     nsHttpVersion  ProxyHttpVersion()        { return mProxyHttpVersion; }
     uint8_t        ReferrerLevel()           { return mReferrerLevel; }
     bool           SpoofReferrerSource()     { return mSpoofReferrerSource; }
     uint8_t        ReferrerTrimmingPolicy()  { return mReferrerTrimmingPolicy; }
+    uint8_t        ReferrerXOriginTrimmingPolicy() {
+        return mReferrerXOriginTrimmingPolicy;
+    }
     uint8_t        ReferrerXOriginPolicy()   { return mReferrerXOriginPolicy; }
     bool           SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
     bool           PackagedAppsEnabled()     { return mPackagedAppsEnabled; }
     uint8_t        RedirectionLimit()        { return mRedirectionLimit; }
     PRIntervalTime IdleTimeout()             { return mIdleTimeout; }
     PRIntervalTime SpdyTimeout()             { return mSpdyTimeout; }
     PRIntervalTime ResponseTimeout() {
       return mResponseTimeoutEnabled ? mResponseTimeout : 0;
@@ -412,16 +415,17 @@ private:
     //
 
     uint8_t  mHttpVersion;
     uint8_t  mProxyHttpVersion;
     uint32_t mCapabilities;
     uint8_t  mReferrerLevel;
     uint8_t  mSpoofReferrerSource;
     uint8_t  mReferrerTrimmingPolicy;
+    uint8_t  mReferrerXOriginTrimmingPolicy;
     uint8_t  mReferrerXOriginPolicy;
 
     bool mFastFallbackToIPv4;
     bool mProxyPipelining;
     PRIntervalTime mIdleTimeout;
     PRIntervalTime mSpdyTimeout;
     PRIntervalTime mResponseTimeout;
     bool mResponseTimeoutEnabled;
--- a/netwerk/test/unit/test_referrer.js
+++ b/netwerk/test/unit/test_referrer.js
@@ -1,19 +1,24 @@
 Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 function getTestReferrer(server_uri, referer_uri) {
   var uri = NetUtil.newURI(server_uri, "", null)
+  let referrer = NetUtil.newURI(referer_uri, null, null);
+  let triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(referrer, {});
   var chan = NetUtil.newChannel({
     uri: uri,
-    loadUsingSystemPrincipal: true
+    loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    triggeringPrincipal: triggeringPrincipal,
+    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
   });
 
   chan.QueryInterface(Components.interfaces.nsIHttpChannel);
-  chan.referrer = NetUtil.newURI(referer_uri, null, null);
+  chan.referrer = referrer;
   var header = null;
   try {
     header = chan.getRequestHeader("Referer");
   }
   catch (NS_ERROR_NOT_AVAILABLE) {}
   return header;
 }
 
@@ -26,16 +31,17 @@ function run_test() {
   var referer_uri = "http://foo.example.com/path";
   var referer_uri_2 = "http://bar.examplesite.com/path3?q=blah";
   var referer_uri_2_anchor = "http://bar.examplesite.com/path3?q=blah#anchor";
   var referer_uri_idn = "http://sub1.\xe4lt.example/path";
 
   // for https tests
   var server_uri_https = "https://bar.example.com/anotherpath";
   var referer_uri_https = "https://bar.example.com/path3?q=blah";
+  var referer_uri_2_https = "https://bar.examplesite.com/path3?q=blah";
 
   // tests for sendRefererHeader
   prefs.setIntPref("network.http.sendRefererHeader", 0);
   do_check_null(getTestReferrer(server_uri, referer_uri));
   prefs.setIntPref("network.http.sendRefererHeader", 2);
   do_check_eq(getTestReferrer(server_uri, referer_uri), referer_uri);
 
   // test that https ref is not sent to http
@@ -67,16 +73,36 @@ function run_test() {
   do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/");
   do_check_eq(getTestReferrer(server_uri, referer_uri_idn), "http://sub1.xn--lt-uia.example/");
   // https test
   do_check_eq(getTestReferrer(server_uri_https, referer_uri_https), "https://bar.example.com/");
   prefs.setIntPref("network.http.referer.trimmingPolicy", 0);
   // test that anchor is lopped off in ordinary case
   do_check_eq(getTestReferrer(server_uri, referer_uri_2_anchor), referer_uri_2);
 
+  // tests for referer.XOriginTrimmingPolicy
+  prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 1);
+  do_check_eq(getTestReferrer(server_uri, referer_uri), "http://foo.example.com/path");
+  do_check_eq(getTestReferrer(server_uri, referer_uri_idn), "http://sub1.xn--lt-uia.example/path");
+  do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3?q=blah");
+  prefs.setIntPref("network.http.referer.trimmingPolicy", 1);
+  do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3");
+  prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 2);
+  do_check_eq(getTestReferrer(server_uri, referer_uri), "http://foo.example.com/");
+  do_check_eq(getTestReferrer(server_uri, referer_uri_idn), "http://sub1.xn--lt-uia.example/");
+  do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3");
+  prefs.setIntPref("network.http.referer.trimmingPolicy", 0);
+  do_check_eq(getTestReferrer(server_uri, referer_uri_2), "http://bar.examplesite.com/path3?q=blah");
+  // https tests
+  do_check_eq(getTestReferrer(server_uri_https, referer_uri_https), "https://bar.example.com/path3?q=blah");
+  do_check_eq(getTestReferrer(server_uri_https, referer_uri_2_https), "https://bar.examplesite.com/");
+  prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 0);
+  // test that anchor is lopped off in ordinary case
+  do_check_eq(getTestReferrer(server_uri, referer_uri_2_anchor), referer_uri_2);
+
   // combination test: send spoofed path-only when hosts match
   var combo_referer_uri = "http://blah.foo.com/path?q=hot";
   var dest_uri = "http://blah.foo.com:9999/spoofedpath?q=bad";
   prefs.setIntPref("network.http.referer.trimmingPolicy", 1);
   prefs.setBoolPref("network.http.referer.spoofSource", true);
   prefs.setIntPref("network.http.referer.XOriginPolicy", 2);
   do_check_eq(getTestReferrer(dest_uri, combo_referer_uri), "http://blah.foo.com:9999/spoofedpath");
   do_check_null(getTestReferrer(dest_uri, "http://gah.foo.com/anotherpath"));