Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement 8.2 Set request’s referrer policy on redirect #1717

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 13 additions & 1 deletion lib/fetch/constants.js
Expand Up @@ -8,6 +8,17 @@ const nullBodyStatus = [101, 204, 205, 304]

const redirectStatus = [301, 302, 303, 307, 308]

// https://fetch.spec.whatwg.org/#block-bad-port
const badPorts = [
'1', '7', '9', '11', '13', '15', '17', '19', '20', '21', '22', '23', '25', '37', '42', '43', '53', '69', '77', '79',
'87', '95', '101', '102', '103', '104', '109', '110', '111', '113', '115', '117', '119', '123', '135', '137',
'139', '143', '161', '179', '389', '427', '465', '512', '513', '514', '515', '526', '530', '531', '532',
'540', '548', '554', '556', '563', '587', '601', '636', '989', '990', '993', '995', '1719', '1720', '1723',
'2049', '3659', '4045', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6697',
'10080'
]

// https://w3c.github.io/webappsec-referrer-policy/#referrer-policies
const referrerPolicy = [
'',
'no-referrer',
Expand Down Expand Up @@ -108,5 +119,6 @@ module.exports = {
redirectStatus,
corsSafeListedMethods,
nullBodyStatus,
safeMethods
safeMethods,
badPorts
}
39 changes: 26 additions & 13 deletions lib/fetch/util.js
@@ -1,6 +1,6 @@
'use strict'

const { redirectStatus } = require('./constants')
const { redirectStatus, badPorts, referrerPolicy: referrerPolicyTokens } = require('./constants')
const { performance } = require('perf_hooks')
const { isBlobLike, toUSVString, ReadableStreamFrom } = require('../core/util')
const assert = require('assert')
Expand All @@ -16,16 +16,6 @@ try {

}

// https://fetch.spec.whatwg.org/#block-bad-port
const badPorts = [
'1', '7', '9', '11', '13', '15', '17', '19', '20', '21', '22', '23', '25', '37', '42', '43', '53', '69', '77', '79',
'87', '95', '101', '102', '103', '104', '109', '110', '111', '113', '115', '117', '119', '123', '135', '137',
'139', '143', '161', '179', '389', '427', '465', '512', '513', '514', '515', '526', '530', '531', '532',
'540', '548', '554', '556', '563', '587', '601', '636', '989', '990', '993', '995', '1719', '1720', '1723',
'2049', '3659', '4045', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6697',
'10080'
]

function responseURL (response) {
// https://fetch.spec.whatwg.org/#responses
// A response has an associated URL. It is a pointer to the last URL
Expand Down Expand Up @@ -200,8 +190,31 @@ function setRequestReferrerPolicyOnRedirect (request, actualResponse) {

// 1. Let policy be the result of executing § 8.1 Parse a referrer policy
// from a Referrer-Policy header on actualResponse.
// TODO: https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header
const policy = ''

// 8.1 Parse a referrer policy from a Referrer-Policy header
// 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` and response’s header list.
const { headersList } = actualResponse
// 2. Let policy be the empty string.
// 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty string, then set policy to token.
// 4. Return policy.
const policyHeader = (headersList.get('referrer-policy') ?? '').split(',')

// Note: As the referrer-policy can contain multiple policies
// separated by comma, we need to loop through all of them
// and pick the first valid one.
// Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#specify_a_fallback_policy
let policy = ''
if (policyHeader.length > 0) {
// The right-most policy takes precedence.
// The left-most policy is the fallback.
for (let i = policyHeader.length; i !== 0; i--) {
KhafraDev marked this conversation as resolved.
Show resolved Hide resolved
const token = policyHeader[i - 1].trim()
KhafraDev marked this conversation as resolved.
Show resolved Hide resolved
if (referrerPolicyTokens.includes(token)) {
policy = token
break
}
}
}

// 2. If policy is not the empty string, then set request’s referrer policy to policy.
if (policy !== '') {
Expand Down
136 changes: 136 additions & 0 deletions test/fetch/util.js
Expand Up @@ -134,6 +134,142 @@ test('isURLPotentiallyTrustworthy', (t) => {
}
})

test('setRequestReferrerPolicyOnRedirect', nested => {
nested.plan(7)

nested.test('should set referrer policy from response headers on redirect', t => {
const request = {
referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'
}

const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
actualResponse.headersList.append('Referrer-Policy', 'origin')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, 'origin')
})

nested.test('should select the first valid policy from a response', t => {
const request = {
referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'
}

const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
actualResponse.headersList.append('Referrer-Policy', 'asdas, origin')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, 'origin')
})

nested.test('should select the first valid policy from a response#2', t => {
const request = {
referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'
}

const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
actualResponse.headersList.append('Referrer-Policy', 'no-referrer, asdas, origin, 0943sd')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, 'origin')
})

nested.test('should pick the last fallback over invalid policy tokens', t => {
const request = {
referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'
}

const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
actualResponse.headersList.append('Referrer-Policy', 'origin, asdas, asdaw34')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, 'origin')
})

nested.test('should set not change request referrer policy if no Referrer-Policy from initial redirect response', t => {
const request = {
referrerPolicy: 'no-referrer, strict-origin-when-cross-origin'
}

const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, 'no-referrer, strict-origin-when-cross-origin')
})

nested.test('should set not change request referrer policy if the policy is a non-valid Referrer Policy', t => {
const initial = 'no-referrer, strict-origin-when-cross-origin'
const request = {
referrerPolicy: initial
}
const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
actualResponse.headersList.append('Referrer-Policy', 'asdasd')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, initial)
})

nested.test('should set not change request referrer policy if the policy is a non-valid Referrer Policy', t => {
const initial = 'no-referrer, strict-origin-when-cross-origin'
const request = {
referrerPolicy: initial
}
const actualResponse = {
headersList: new HeadersList()
}

t.plan(1)

actualResponse.headersList.append('Connection', 'close')
actualResponse.headersList.append('Location', 'https://some-location.com/redirect')
actualResponse.headersList.append('Referrer-Policy', 'asdasd, asdasa, 12daw,')
util.setRequestReferrerPolicyOnRedirect(request, actualResponse)

t.equal(request.referrerPolicy, initial)
})
})

test('determineRequestsReferrer', (t) => {
t.plan(7)

Expand Down