Bug 1416344 - refactor computing referrer policy and remove uninitilized maybe value r=valentin

MozReview-Commit-ID: 7VoRaUSE096

--HG--
extra : rebase_source : 2c0aa81b5751fe187494df75e6d853b64f5b9b47
This commit is contained in:
Thomas Nguyen 2017-11-13 19:23:47 +08:00
parent 819392126d
commit 5bd3a1a2c6
4 changed files with 387 additions and 77 deletions

View File

@ -1565,6 +1565,36 @@ HttpBaseChannel::GetReferrerPolicy(uint32_t *referrerPolicy)
return NS_OK;
}
/* Computing whether our URI is cross-origin may be expensive, so please do
* that in cases where we're going to use this information later on.
*/
bool
HttpBaseChannel::IsCrossOriginWithReferrer()
{
nsresult rv;
nsCOMPtr<nsIURI> triggeringURI;
if (mLoadInfo) {
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mLoadInfo->TriggeringPrincipal();
if (triggeringPrincipal) {
triggeringPrincipal->GetURI(getter_AddRefs(triggeringURI));
}
}
if (triggeringURI) {
if (LOG_ENABLED()) {
nsAutoCString triggeringURISpec;
triggeringURI->GetAsciiSpec(triggeringURISpec);
LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
rv = ssm->CheckSameOriginURI(triggeringURI, mURI, false);
return (NS_FAILED(rv));
}
LOG(("no triggering principal available via loadInfo, assuming load is cross-origin"));
return true;
}
NS_IMETHODIMP
HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
uint32_t referrerPolicy)
@ -1606,11 +1636,6 @@ HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
// true: use an empty referrer
bool userHideOnionReferrerSource = gHttpHandler->HideOnionReferrerSource();
// 0: full URI
// 1: scheme+host+port+path
// 2: scheme+host+port
int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy();
// 0: send referer no matter what
// 1: send referer ONLY when base domains match
// 2: send referer ONLY when hosts match
@ -1767,85 +1792,57 @@ HttpBaseChannel::SetReferrerWithPolicy(nsIURI *referrer,
rv = clone->SetUserPass(EmptyCString());
if (NS_FAILED(rv)) return rv;
// Computing whether our URI is cross-origin may be expensive, so we only do
// that in cases where we're going to use this information later on. The if
// condition below encodes those cases. isCrossOrigin.isNothing() will return
// true otherwise.
Maybe<bool> isCrossOrigin;
if ((mReferrerPolicy == REFERRER_POLICY_SAME_ORIGIN ||
mReferrerPolicy == REFERRER_POLICY_ORIGIN_WHEN_XORIGIN ||
mReferrerPolicy == REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN ||
// If our referrer policy is origin-only or strict-origin, we will send
// the origin only no matter if we are cross origin, so in those cases we
// can also skip checking cross-origin-ness.
(gHttpHandler->ReferrerXOriginTrimmingPolicy() != 0 &&
mReferrerPolicy != REFERRER_POLICY_ORIGIN &&
mReferrerPolicy != REFERRER_POLICY_STRICT_ORIGIN)) &&
// 2 (origin-only) is already the strictest policy which we'd adopt if we
// were cross-origin, so there is no point to compute whether we are or
// not.
gHttpHandler->ReferrerTrimmingPolicy() != 2) {
// for cross-origin-based referrer changes (not just host-based), figure out
// if the referrer is being sent cross-origin.
nsCOMPtr<nsIURI> triggeringURI;
if (mLoadInfo) {
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mLoadInfo->TriggeringPrincipal();
if (triggeringPrincipal) {
triggeringPrincipal->GetURI(getter_AddRefs(triggeringURI));
// 0: full URI
// 1: scheme+host+port+path
// 2: scheme+host+port
int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy();
int userReferrerXOriginTrimmingPolicy =
gHttpHandler->ReferrerXOriginTrimmingPolicy();
switch (mReferrerPolicy) {
case REFERRER_POLICY_SAME_ORIGIN:
// Don't send referrer when the request is cross-origin and policy is "same-origin".
if (IsCrossOriginWithReferrer()) {
return NS_OK;
}
}
if (triggeringURI) {
if (LOG_ENABLED()) {
nsAutoCString triggeringURISpec;
rv = triggeringURI->GetAsciiSpec(triggeringURISpec);
if (!NS_FAILED(rv)) {
LOG(("triggeringURI=%s\n", triggeringURISpec.get()));
break;
case REFERRER_POLICY_ORIGIN:
case REFERRER_POLICY_STRICT_ORIGIN:
userReferrerTrimmingPolicy = 2;
break;
case REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
case REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN:
if (userReferrerTrimmingPolicy != 2 && IsCrossOriginWithReferrer()) {
// Ignore set userReferrerTrimmingPolicy if it is already the strictest
// policy.
userReferrerTrimmingPolicy = 2;
}
break;
case REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
case REFERRER_POLICY_UNSAFE_URL:
if (userReferrerTrimmingPolicy != 2) {
// Ignore set userReferrerTrimmingPolicy if it is already the strictest
// policy. Apply the user cross-origin trimming policy if it's more
// restrictive than the general one.
if (userReferrerXOriginTrimmingPolicy != 0 && IsCrossOriginWithReferrer()) {
userReferrerTrimmingPolicy =
std::max(userReferrerTrimmingPolicy, userReferrerXOriginTrimmingPolicy);
}
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
rv = ssm->CheckSameOriginURI(triggeringURI, mURI, false);
isCrossOrigin.emplace(NS_FAILED(rv));
} else {
LOG(("no triggering principal available via loadInfo, assuming load is cross-origin"));
isCrossOrigin.emplace(true);
}
}
// Don't send referrer when the request is cross-origin and policy is "same-origin".
if (mReferrerPolicy == REFERRER_POLICY_SAME_ORIGIN && *isCrossOrigin) {
return NS_OK;
break;
case REFERRER_POLICY_NO_REFERRER:
case REFERRER_POLICY_UNSET:
default:
MOZ_ASSERT_UNREACHABLE("Unexpected value");
break;
}
nsAutoCString spec;
// Apply the user cross-origin trimming policy if it's more
// restrictive than the general one.
int userReferrerXOriginTrimmingPolicy =
gHttpHandler->ReferrerXOriginTrimmingPolicy();
if (userReferrerXOriginTrimmingPolicy != 0 && *isCrossOrigin) {
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"
if (mReferrerPolicy == REFERRER_POLICY_ORIGIN ||
mReferrerPolicy == REFERRER_POLICY_STRICT_ORIGIN ||
((mReferrerPolicy == REFERRER_POLICY_ORIGIN_WHEN_XORIGIN ||
mReferrerPolicy == REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN) &&
*isCrossOrigin)) {
// We can override the user trimming preference because "origin"
// (network.http.referer.trimmingPolicy = 2) is the strictest
// trimming policy that users can specify.
userReferrerTrimmingPolicy = 2;
}
// check how much referer to send
if (userReferrerTrimmingPolicy) {
// All output strings start with: scheme+host+port

View File

@ -498,6 +498,8 @@ private:
// Proxy release all members above on main thread.
void ReleaseMainThreadOnlyReferences();
bool IsCrossOriginWithReferrer();
protected:
// Use Release-Acquire ordering to ensure the OMT ODA is ignored while channel
// is canceled on main thread.

View File

@ -0,0 +1,310 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function test_policy(test) {
do_print("Running test: " + test.toSource());
let prefs = Services.prefs;
if (test.trimmingPolicy !== undefined) {
prefs.setIntPref("network.http.referer.trimmingPolicy",
test.trimmingPolicy);
} else {
prefs.setIntPref("network.http.referer.trimmingPolicy", 0);
}
if (test.XOriginTrimmingPolicy !== undefined) {
prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy",
test.XOriginTrimmingPolicy);
} else {
prefs.setIntPref("network.http.referer.XOriginTrimmingPolicy", 0);
}
let referrer = NetUtil.newURI(test.referrer);
let triggeringPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(referrer, {});
let chan = NetUtil.newChannel({
uri: test.url,
loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
triggeringPrincipal: triggeringPrincipal,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
});
chan.QueryInterface(Components.interfaces.nsIHttpChannel);
chan.setReferrerWithPolicy(referrer, test.policy);
if (test.expectedReferrerSpec === undefined) {
try {
chan.getRequestHeader("Referer");
do_throw("Should not find a Referer header!");
} catch(e) {
}
do_check_eq(chan.referrer, null);
} else {
let header = chan.getRequestHeader("Referer");
do_check_eq(header, test.expectedReferrerSpec);
do_check_eq(chan.referrer.asciiSpec, test.expectedReferrerSpec);
}
}
const nsIHttpChannel = Ci.nsIHttpChannel;
var gTests = [
// Test same origin policy w/o cross origin
{
policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
{
policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
{
policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_SAME_ORIGIN,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
// Test origin when xorigin policy w/o cross origin
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
// Test strict origin when xorigin policy w/o cross origin
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
url: "http://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 1,
url: "http://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
trimmingPolicy: 2,
url: "http://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 1,
url: "http://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo?a"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: "https://foo.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN,
XOriginTrimmingPolicy: 2,
url: "http://test.example/foo?a",
referrer: "https://foo.example/foo?a",
expectedReferrerSpec: undefined
},
// Test mix and choose max of XOriginTrimmingPolicy and trimmingPolicy
{
policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
XOriginTrimmingPolicy: 2,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test1.example/foo?a",
expectedReferrerSpec: "https://test1.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
XOriginTrimmingPolicy: 2,
trimmingPolicy: 1,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/foo"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
XOriginTrimmingPolicy: 1,
trimmingPolicy: 2,
url: "https://test.example/foo?a",
referrer: "https://test.example/foo?a",
expectedReferrerSpec: "https://test.example/"
},
{
policy: nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
XOriginTrimmingPolicy: 1,
trimmingPolicy: 0,
url: "https://test.example/foo?a",
referrer: "https://test1.example/foo?a",
expectedReferrerSpec: "https://test1.example/foo"
},
];
function run_test() {
gTests.forEach(test => test_policy(test));
Services.prefs.clearUserPref("network.http.referer.trimmingPolicy");
Services.prefs.clearUserPref("network.http.referer.XOriginTrimmingPolicy");
}

View File

@ -338,6 +338,7 @@ skip-if = os == "android"
[test_about_networking.js]
[test_ping_aboutnetworking.js]
[test_referrer.js]
[test_referrer_cross_origin.js]
[test_referrer_policy.js]
[test_predictor.js]
# Android version detection w/in gecko does not work right on infra, so we just