Skip to content

Commit

Permalink
feat: implement 8.2 Set request’s referrer policy on redirect (node…
Browse files Browse the repository at this point in the history
…js#1717)

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

* feat: extend to double-check if a valid referrer policy

* refactor: apply review suggestions

* feat: add support for fallback policies
  • Loading branch information
metcoder95 committed Dec 26, 2022
1 parent 9b659a4 commit 5cfb11e
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 14 deletions.
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--) {
const token = policyHeader[i - 1].trim()
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

0 comments on commit 5cfb11e

Please sign in to comment.