Bug 1495880 - update our cors/no-cors header safelisting to match the Fetch spec; r=hsivonen,ckerschb

update XHR cors/no-cors header safelisting to match the Fetch spec

Differential Revision: https://phabricator.services.mozilla.com/D7771
This commit is contained in:
Thomas Wisniewski
2019-04-01 05:20:15 +00:00
parent 29db1ad763
commit 7bd9ab3a1a
12 changed files with 118 additions and 67 deletions

View File

@@ -6942,6 +6942,23 @@ bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
return true;
}
// static
bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
const nsACString& aValue) {
// see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
if (aValue.Length() > 128) {
return false;
}
return (aName.LowerCaseEqualsLiteral("accept") &&
nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
(aName.LowerCaseEqualsLiteral("accept-language") &&
nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
(aName.LowerCaseEqualsLiteral("content-language") &&
nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
(aName.LowerCaseEqualsLiteral("content-type") &&
nsContentUtils::IsAllowedNonCorsContentType(aValue));
}
bool nsContentUtils::DoNotTrackEnabled() {
return nsContentUtils::sDoNotTrackEnabled;
}

View File

@@ -2759,6 +2759,13 @@ class nsContentUtils {
*/
static bool IsAllowedNonCorsLanguage(const nsACString& aHeaderValue);
/**
* Returns whether a given header and value is a CORS-safelisted request
* header per https://fetch.spec.whatwg.org/#cors-safelisted-request-header
*/
static bool IsCORSSafelistedRequestHeader(const nsACString& aName,
const nsACString& aValue);
/**
* Returns whether a given header is forbidden for an XHR or fetch
* response.

View File

@@ -3931,20 +3931,9 @@ void RequestHeaders::ApplyToChannel(nsIHttpChannel* aHttpChannel) const {
}
void RequestHeaders::GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const {
static const char* kCrossOriginSafeHeaders[] = {
"accept", "accept-language", "content-language", "content-type",
"last-event-id"};
const uint32_t kCrossOriginSafeHeadersLength =
ArrayLength(kCrossOriginSafeHeaders);
for (const RequestHeader& header : mHeaders) {
bool safe = false;
for (uint32_t i = 0; i < kCrossOriginSafeHeadersLength; ++i) {
if (header.mName.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
safe = true;
break;
}
}
if (!safe) {
if (!nsContentUtils::IsCORSSafelistedRequestHeader(header.mName,
header.mValue)) {
aArray.AppendElement(header.mName);
}
}

View File

@@ -1,4 +1,4 @@
[client-hint-request-headers.htm]
[client-hint-request-headers-2.tentative.htm]
[Client hint headers are simple headers]
expected: FAIL

View File

@@ -1,4 +1,4 @@
[simple-requests.htm]
[simple-requests-ch.tentative.htm]
[No preflight GET and {"save-data":"on","device-memory":"2.0","dpr":"3.0","width":"1200","viewport-width":"1300"}]
expected: FAIL

View File

@@ -1,4 +0,0 @@
[access-control-basic-cors-safelisted-request-headers.htm]
[Request with CORS-safelisted headers]
expected: FAIL

View File

@@ -1,10 +0,0 @@
[send-redirect-to-cors.htm]
[XMLHttpRequest: send() - Redirect to CORS-enabled resource (301 GET with explicit Content-Type)]
expected: FAIL
[XMLHttpRequest: send() - Redirect to CORS-enabled resource (301 POST with string and explicit Content-Type)]
expected: FAIL
[XMLHttpRequest: send() - Redirect to CORS-enabled resource (302 POST with string and explicit Content-Type)]
expected: FAIL

View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>CORS and Client Hints, potentially</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=support.js?pipe=sub></script>
<h1>Request headers</h1>
<div id=log></div>
<script>
test(function() {
var client = new XMLHttpRequest()
client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print,', false)
client.setRequestHeader('x-print', 'unicorn')
client.setRequestHeader('content-type', 'text/plain')
client.setRequestHeader('accept', 'test')
client.setRequestHeader('accept-language', 'nn')
client.setRequestHeader('content-language', 'nn')
client.setRequestHeader('save-data', 'on')
client.setRequestHeader('device-memory', '1.0')
client.setRequestHeader('dpr', '2.0')
client.setRequestHeader('width', '35')
client.setRequestHeader('viewport-width', '42')
client.send(null)
const res = JSON.parse(client.response)
assert_equals(res['x-print'], 'unicorn')
assert_equals(res['content-type'], 'text/plain')
assert_equals(res['accept'], 'test')
assert_equals(res['accept-language'], 'nn')
assert_equals(res['content-language'], 'nn')
assert_equals(res['save-data'], 'on')
assert_equals(res['device-memory'], '1.0')
assert_equals(res['dpr'], '2.0')
assert_equals(res['width'], '35')
assert_equals(res['viewport-width'], '42')
}, 'Client hint headers are simple headers')
</script>

View File

@@ -1,6 +1,6 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>CORS - client hint request headers - Access-Control-Allow-Headers</title>
<title>CORS and Client Hints</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
@@ -10,34 +10,6 @@
<div id=log></div>
<script>
test(function() {
var client = new XMLHttpRequest()
client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print,', false)
client.setRequestHeader('x-print', 'unicorn')
client.setRequestHeader('content-type', 'text/plain')
client.setRequestHeader('accept', 'test')
client.setRequestHeader('accept-language', 'nn')
client.setRequestHeader('content-language', 'nn')
client.setRequestHeader('save-data', 'on')
client.setRequestHeader('device-memory', '1.0')
client.setRequestHeader('dpr', '2.0')
client.setRequestHeader('width', '35')
client.setRequestHeader('viewport-width', '42')
client.send(null)
const res = JSON.parse(client.response)
assert_equals(res['x-print'], 'unicorn')
assert_equals(res['content-type'], 'text/plain')
assert_equals(res['accept'], 'test')
assert_equals(res['accept-language'], 'nn')
assert_equals(res['content-language'], 'nn')
assert_equals(res['save-data'], 'on')
assert_equals(res['device-memory'], '1.0')
assert_equals(res['dpr'], '2.0')
assert_equals(res['width'], '35')
assert_equals(res['viewport-width'], '42')
}, 'Client hint headers are simple headers')
test(function() {
var client = new XMLHttpRequest()
client.open('GET', CROSSDOMAIN + 'resources/cors-makeheader.py?headers=x-print', false)

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>CORS - simple requests</title>
<meta name=author title="Odin Hørthe Omdal" href="mailto:odiho@opera.com">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=support.js?pipe=sub></script>
<script src=/common/utils.js></script>
<h1>Simple requests</h1>
<p>Simple requests shouldn't trigger preflight</p>
<div id=log></div>
<script>
var test_c = 0;
function check_simple(method, headers)
{
test(function() {
var client = new XMLHttpRequest()
var uuid_token = token();
client.open(method, CROSSDOMAIN + 'resources/preflight.py?token='
+ uuid_token, false)
for (head in headers)
client.setRequestHeader(head, headers[head])
client.send("data")
assert_equals(client.getResponseHeader('content-type'), "text/plain")
if (method == 'HEAD')
assert_equals(client.response, '', 'response')
else
assert_equals(client.response, 'NO', 'response')
client.open('GET', 'resources/preflight.py?check&token='
+ uuid_token, false)
client.send("data")
assert_equals(client.response, "0", "Found preflight log")
},
'No preflight ' + method + ' and ' + JSON.stringify(headers))
}
function check_simple_headers(headers) {
check_simple('GET', headers)
check_simple('HEAD', headers)
check_simple('POST', headers)
}
check_simple_headers({
'save-data': 'on',
'device-memory': '2.0',
'dpr': '3.0',
'width': '1200',
'viewport-width': '1300'
})
</script>

View File

@@ -61,14 +61,6 @@ check_simple_headers({
'content-type': 'text/plain; parameter=whatever'
})
check_simple_headers({
'save-data': 'on',
'device-memory': '2.0',
'dpr': '3.0',
'width': '1200',
'viewport-width': '1300'
})
check_simple('Get', {'content-type': 'text/plain; parameter=extra_bonus'})
check_simple('post', {'content-type': 'text/plain'})

View File

@@ -17,7 +17,6 @@
xhr.setRequestHeader("Accept-Language", "ru");
xhr.setRequestHeader("Content-Language", "ru");
xhr.setRequestHeader("Content-Type", "text/plain");
xhr.setRequestHeader("Save-Data", "on");
xhr.send();