Bug 1223838 - Fix wrong policy associated with empty string. r=fkiefer,hsivonen

MozReview-Commit-ID: 7kFH39cegmH
This commit is contained in:
Thomas Nguyen 2016-05-30 15:17:45 +08:00
parent 8e14007b6d
commit 4b7ad0e2c5
10 changed files with 165 additions and 74 deletions

View File

@ -3781,15 +3781,17 @@ nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
// Referrer policy spec says to ignore any empty referrer policies.
if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
// Referrer policy spec (section 6.1) says that we always use the newest
// referrer policy we find
mReferrerPolicy = policy;
mReferrerPolicySet = true;
ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
// If policy is not the empty string, then set element's node document's
// referrer policy to policy
if (policy != mozilla::net::RP_Unset) {
// Referrer policy spec (section 6.1) says that we always use the newest
// referrer policy we find
mReferrerPolicy = policy;
mReferrerPolicySet = true;
}
}
}
void
nsDocument::TryChannelCharset(nsIChannel *aChannel,
int32_t& aCharsetSource,

View File

@ -93,10 +93,11 @@ function createTestPage2(aHead, aPolicy, aName) {
</html>';
}
function createTestPage3(aPolicy, aName) {
function createTestPage3(aHead, aPolicy, aName) {
return '<!DOCTYPE HTML>\n\
<html>\n\
<body>\n\
<html>'+
aHead +
'<body>\n\
<script>' +
'var image = new Image();\n\
image.src = "' + createTestUrl(aPolicy, "test", aName, "image") + '";\n\
@ -111,10 +112,11 @@ function createTestPage3(aPolicy, aName) {
</html>';
}
function createTestPage4(aPolicy, aName) {
function createTestPage4(aHead, aPolicy, aName) {
return '<!DOCTYPE HTML>\n\
<html>\n\
<body>\n\
<html>'+
aHead +
'<body>\n\
<script>' +
'var image = new Image();\n\
image.referrerPolicy = "' + aPolicy + '";\n\
@ -129,6 +131,23 @@ function createTestPage4(aPolicy, aName) {
</html>';
}
function createSetAttributeTest1(aPolicy, aImgPolicy, aName) {
var headString = '<head>';
headString += '<meta name="referrer" content="' + aPolicy + '">';
headString += '<script></script>';
return createTestPage3(headString, aImgPolicy, aName);
}
function createSetAttributeTest2(aPolicy, aImgPolicy, aName) {
var headString = '<head>';
headString += '<meta name="referrer" content="' + aPolicy + '">';
headString += '<script></script>';
return createTestPage4(headString, aImgPolicy, aName);
}
function createTest4(aPolicy, aName) {
var headString = '<head>';
headString += '<meta name="referrer" content="' + aPolicy + '">';
@ -256,19 +275,21 @@ function handleRequest(request, response) {
if (action === 'generate-setAttribute-test1') {
// ?action=generate-setAttribute-test1&policy=b64-encoded-string&name=name
var policy = unescape(params[1].split('=')[1]);
var name = unescape(params[2].split('=')[1]);
var imgPolicy = unescape(params[1].split('=')[1]);
var policy = unescape(params[2].split('=')[1]);
var name = unescape(params[3].split('=')[1]);
response.write(createTestPage3(policy, name));
response.write(createSetAttributeTest1(policy, imgPolicy, name));
return;
}
if (action === 'generate-setAttribute-test2') {
// ?action=generate-setAttribute-test2&policy=b64-encoded-string&name=name
var policy = unescape(params[1].split('=')[1]);
var name = unescape(params[2].split('=')[1]);
var imgPolicy = unescape(params[1].split('=')[1]);
var policy = unescape(params[2].split('=')[1]);
var name = unescape(params[3].split('=')[1]);
response.write(createTestPage4(policy, name));
response.write(createSetAttributeTest2(policy, imgPolicy, name));
return;
}

View File

@ -584,7 +584,6 @@ skip-if = buildapp == 'b2g' # b2g (https://example.com not working bug 1162353)
skip-if = buildapp == 'b2g' # b2g (https://example.com not working bug 1162353)
[test_bug704320_policyset.html]
[test_bug704320_policyset2.html]
skip-if = os == "mac" # fails intermittently - bug 1101288
[test_bug704320_preload.html]
[test_bug707142.html]
[test_bug708620.html]

View File

@ -40,12 +40,12 @@ var tests = (function() {
yield checkIndividualResults("default", ["full"]);
// check invalid policy
// According to the spec section 6.4, if there is a policy token
// and it is not one of the expected tokens, "No Referrer"
// should be the policy used.
// According to the spec section Determine token's Policy,if there is a policy
// token and it is not one of the expected tokens, Empty string should be the
// policy used.
yield resetCounter();
yield iframe.src = sjs + "&policy=" + escape('invalid-policy');
yield checkIndividualResults("invalid", ["none"]);
yield checkIndividualResults("invalid", ["full"]);
// whitespace checks.
// according to the spec section 4.1, the content attribute's value
@ -84,6 +84,11 @@ var tests = (function() {
yield iframe.src = sjs + "&policy=" + escape('no-referrer');
yield checkIndividualResults("no-referrer", ["none"]);
// Case insensitive
yield resetCounter();
yield iframe.src = sjs + "&policy=" + escape('\f OrigIn');
yield checkIndividualResults("origin case insensitive", ["origin"]);
// complete. Be sure to yield so we don't call this twice.
yield SimpleTest.finish();
})();

View File

@ -151,15 +151,29 @@ var tests = (function() {
yield resetState();
sjs = "/tests/dom/base/test/img_referrer_testserver.sjs?action=generate-setAttribute-test1";
name = 'set-referrer-policy-attribute-before-src';
yield iframe.src = sjs + "&policy=" + escape('no-referrer') + "&name=" + name;
yield iframe.src = sjs + "&imgPolicy=" + escape('no-referrer') + "&policy=" + escape('unsafe-url') + "&name=" + name;
yield checkIndividualResults("no-referrer in img", ["none"], [name]);
yield resetState();
sjs = "/tests/dom/base/test/img_referrer_testserver.sjs?action=generate-setAttribute-test2";
name = 'set-referrer-policy-attribute-after-src';
yield iframe.src = sjs + "&policy=" + escape('no-referrer') + "&name=" + name;
yield iframe.src = sjs + "&imgPolicy=" + escape('no-referrer') + "&policy=" + escape('unsafe-url') + "&name=" + name;
yield checkIndividualResults("no-referrer in img", ["none"], [name]);
yield resetState();
sjs =
"/tests/dom/base/test/img_referrer_testserver.sjs?action=generate-setAttribute-test2";
name = 'set-invalid-referrer-policy-attribute-before-src-invalid';
yield iframe.src = sjs + "&imgPolicy=" + escape('invalid') + "&policy=" + escape('unsafe-url') + "&name=" + name;
yield checkIndividualResults("unsafe-url in meta, invalid in img", ["full"], [name]);
yield resetState();
sjs =
"/tests/dom/base/test/img_referrer_testserver.sjs?action=generate-setAttribute-test2";
name = 'set-invalid-referrer-policy-attribute-before-src-invalid';
yield iframe.src = sjs + "&imgPolicy=" + escape('default') + "&policy=" + escape('unsafe-url') + "&name=" + name;
yield checkIndividualResults("unsafe-url in meta, default in img", ["full"], [name]);
// complete. Be sure to yield so we don't call this twice.
yield SimpleTest.finish();
})();

View File

@ -555,8 +555,10 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
} else if (aName == nsGkAtoms::referrerpolicy &&
aNameSpaceID == kNameSpaceID_None &&
aNotify) {
ReferrerPolicy referrerPolicy = ReferrerPolicyFromString(aValue);
if (!InResponsiveMode() && referrerPolicy != GetImageReferrerPolicy()) {
ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue);
if (!InResponsiveMode() &&
referrerPolicy != RP_Unset &&
referrerPolicy != GetImageReferrerPolicy()) {
// XXX: Bug 1076583 - We still use the older synchronous algorithm
// Because referrerPolicy is not treated as relevant mutations, setting
// the attribute will neither trigger a reload nor update the referrer

View File

@ -342,8 +342,21 @@ nsCSPContext::GetReferrerPolicy(uint32_t* outPolicy, bool* outIsSet)
// an empty string in refpol means it wasn't set (that's the default in
// nsCSPPolicy).
if (!refpol.IsEmpty()) {
// if there are two policies that specify a referrer policy, then they
// Referrer Directive in CSP is no more used and going to be replaced by
// Referrer-Policy HTTP header. But we still keep using referrer directive,
// and would remove it later.
// Referrer Directive specs is not fully compliant with new referrer policy
// specs. What we are using here:
// - If the value of the referrer directive is invalid, the user agent
// should set the referrer policy to no-referrer.
// - If there are two policies that specify a referrer policy, then they
// must agree or the employed policy is no-referrer.
if (!mozilla::net::IsValidReferrerPolicy(refpol)) {
*outPolicy = mozilla::net::RP_No_Referrer;
*outIsSet = true;
return NS_OK;
}
uint32_t currentPolicy = mozilla::net::ReferrerPolicyFromString(refpol);
if (*outIsSet && previousPolicy != currentPolicy) {
*outPolicy = mozilla::net::RP_No_Referrer;

View File

@ -2022,7 +2022,8 @@ imgLoader::LoadImageXPCOM(nsIURI* aURI,
nsresult rv = LoadImage(aURI,
aInitialDocumentURI,
aReferrerURI,
refpol,
refpol == mozilla::net::RP_Unset ?
mozilla::net::RP_Default : refpol,
aLoadingPrincipal,
aLoadGroup,
aObserver,

View File

@ -7,6 +7,7 @@
#include "nsStringGlue.h"
#include "nsIHttpChannel.h"
#include "nsUnicharUtils.h"
namespace mozilla { namespace net {
@ -27,7 +28,8 @@ enum ReferrerPolicy {
/* spec tokens: always unsafe-url */
RP_Unsafe_URL = nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL,
/* referrer policy is not set */
/* spec tokens: empty string */
/* The empty string "" corresponds to no referrer policy, or unset policy */
RP_Unset = nsIHttpChannel::REFERRER_POLICY_UNSET,
};
@ -53,71 +55,89 @@ const char kRPS_Unsafe_URL[] = "unsafe-url";
inline ReferrerPolicy
ReferrerPolicyFromString(const nsAString& content)
{
// This is implemented step by step as described in the Referrer Policy
// specification, section 6.4 "Determine token's Policy".
if (content.LowerCaseEqualsLiteral(kRPS_Never) ||
content.LowerCaseEqualsLiteral(kRPS_No_Referrer)) {
if (content.IsEmpty()) {
return RP_No_Referrer;
}
if (content.LowerCaseEqualsLiteral(kRPS_Origin)) {
nsString lowerContent(content);
ToLowerCase(lowerContent);
// This is implemented step by step as described in the Referrer Policy
// specification, section "Determine token's Policy".
if (lowerContent.EqualsLiteral(kRPS_Never) ||
lowerContent.EqualsLiteral(kRPS_No_Referrer)) {
return RP_No_Referrer;
}
if (lowerContent.EqualsLiteral(kRPS_Origin)) {
return RP_Origin;
}
if (content.LowerCaseEqualsLiteral(kRPS_Default) ||
content.LowerCaseEqualsLiteral(kRPS_No_Referrer_When_Downgrade)) {
if (lowerContent.EqualsLiteral(kRPS_Default) ||
lowerContent.EqualsLiteral(kRPS_No_Referrer_When_Downgrade)) {
return RP_No_Referrer_When_Downgrade;
}
if (content.LowerCaseEqualsLiteral(kRPS_Origin_When_Cross_Origin) ||
content.LowerCaseEqualsLiteral(kRPS_Origin_When_Crossorigin)) {
if (lowerContent.EqualsLiteral(kRPS_Origin_When_Cross_Origin) ||
lowerContent.EqualsLiteral(kRPS_Origin_When_Crossorigin)) {
return RP_Origin_When_Crossorigin;
}
if (content.LowerCaseEqualsLiteral(kRPS_Always) ||
content.LowerCaseEqualsLiteral(kRPS_Unsafe_URL)) {
if (lowerContent.EqualsLiteral(kRPS_Always) ||
lowerContent.EqualsLiteral(kRPS_Unsafe_URL)) {
return RP_Unsafe_URL;
}
// Spec says if none of the previous match, use No_Referrer.
return RP_No_Referrer;
// Spec says if none of the previous match, use empty string.
return RP_Unset;
}
inline bool
IsValidReferrerPolicy(const nsAString& content)
{
return content.LowerCaseEqualsLiteral(kRPS_Never)
|| content.LowerCaseEqualsLiteral(kRPS_No_Referrer)
|| content.LowerCaseEqualsLiteral(kRPS_Origin)
|| content.LowerCaseEqualsLiteral(kRPS_Default)
|| content.LowerCaseEqualsLiteral(kRPS_No_Referrer_When_Downgrade)
|| content.LowerCaseEqualsLiteral(kRPS_Origin_When_Cross_Origin)
|| content.LowerCaseEqualsLiteral(kRPS_Origin_When_Crossorigin)
|| content.LowerCaseEqualsLiteral(kRPS_Always)
|| content.LowerCaseEqualsLiteral(kRPS_Unsafe_URL);
}
if (content.IsEmpty()) {
return true;
}
inline bool
IsValidAttributeReferrerPolicy(const nsAString& aContent)
{
return aContent.LowerCaseEqualsLiteral(kRPS_No_Referrer)
|| aContent.LowerCaseEqualsLiteral(kRPS_Origin)
|| aContent.LowerCaseEqualsLiteral(kRPS_No_Referrer_When_Downgrade)
|| aContent.LowerCaseEqualsLiteral(kRPS_Origin_When_Cross_Origin)
|| aContent.LowerCaseEqualsLiteral(kRPS_Unsafe_URL);
nsString lowerContent(content);
ToLowerCase(lowerContent);
return lowerContent.EqualsLiteral(kRPS_Never)
|| lowerContent.EqualsLiteral(kRPS_No_Referrer)
|| lowerContent.EqualsLiteral(kRPS_Origin)
|| lowerContent.EqualsLiteral(kRPS_Default)
|| lowerContent.EqualsLiteral(kRPS_No_Referrer_When_Downgrade)
|| lowerContent.EqualsLiteral(kRPS_Origin_When_Cross_Origin)
|| lowerContent.EqualsLiteral(kRPS_Origin_When_Crossorigin)
|| lowerContent.EqualsLiteral(kRPS_Always)
|| lowerContent.EqualsLiteral(kRPS_Unsafe_URL);
}
inline ReferrerPolicy
AttributeReferrerPolicyFromString(const nsAString& aContent)
AttributeReferrerPolicyFromString(const nsAString& content)
{
// if the referrer attribute string is empty, return RP_Unset
if (aContent.IsEmpty()) {
// Specs : https://html.spec.whatwg.org/multipage/infrastructure.html#referrer-policy-attribute
// Spec says the empty string "" corresponds to no referrer policy, or RP_Unset
if (content.IsEmpty()) {
return RP_Unset;
}
// if the referrer attribute string is not empty and contains a valid
// referrer policy, return the according enum value
if (IsValidAttributeReferrerPolicy(aContent)) {
return ReferrerPolicyFromString(aContent);
nsString lowerContent(content);
ToLowerCase(lowerContent);
if (lowerContent.EqualsLiteral(kRPS_No_Referrer)) {
return RP_No_Referrer;
}
// in any other case the referrer attribute contains an invalid
// policy value, we thus return RP_No_Referrer
return RP_No_Referrer;
if (lowerContent.EqualsLiteral(kRPS_Origin)) {
return RP_Origin;
}
if (lowerContent.EqualsLiteral(kRPS_No_Referrer_When_Downgrade)) {
return RP_No_Referrer_When_Downgrade;
}
if (lowerContent.EqualsLiteral(kRPS_Origin_When_Cross_Origin)) {
return RP_Origin_When_Crossorigin;
}
if (lowerContent.EqualsLiteral(kRPS_Unsafe_URL)) {
return RP_Unsafe_URL;
}
// Spec says invalid value default is empty string state
// So, return RP_Unset if none of the previous match, return RP_Unset
return RP_Unset;
}
} // namespace net

View File

@ -952,7 +952,8 @@ nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
// if enabled in preferences, use the referrer attribute from the image, if provided
bool referrerAttributeEnabled = Preferences::GetBool("network.http.enablePerElementReferrer", true);
if (referrerAttributeEnabled) {
mozilla::net::ReferrerPolicy imageReferrerPolicy = mozilla::net::ReferrerPolicyFromString(aImageReferrerPolicy);
mozilla::net::ReferrerPolicy imageReferrerPolicy =
mozilla::net::AttributeReferrerPolicyFromString(aImageReferrerPolicy);
if (imageReferrerPolicy != mozilla::net::RP_Unset) {
referrerPolicy = imageReferrerPolicy;
}
@ -1011,8 +1012,21 @@ nsHtml5TreeOpExecutor::SetSpeculationBase(const nsAString& aURL)
void
nsHtml5TreeOpExecutor::SetSpeculationReferrerPolicy(const nsAString& aReferrerPolicy)
{
// Specs says:
// - Let value be the result of stripping leading and trailing whitespace from
// the value of element's content attribute.
// - If value is not the empty string, then:
if (aReferrerPolicy.IsEmpty()) {
return;
}
ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aReferrerPolicy);
return SetSpeculationReferrerPolicy(policy);
// Specs says:
// - If policy is not the empty string, then set element's node document's
// referrer policy to policy
if (policy != mozilla::net::RP_Unset) {
SetSpeculationReferrerPolicy(policy);
}
}
void