diff --git a/lib/fetch/util.js b/lib/fetch/util.js index c738a378178..a738c077e02 100644 --- a/lib/fetch/util.js +++ b/lib/fetch/util.js @@ -197,18 +197,29 @@ function setRequestReferrerPolicyOnRedirect (request, 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 policy = headersList.get('referrer-policy') ?? '' - - // 2. If policy is not the empty string, then set request’s referrer policy to policy. - if (policy !== '') { - for (const policyToken of referrerPolicyTokens) { - // if token is a referrer policy and token is not an empty string, then set policy to token. - if (policy === policyToken) { - request.referrerPolicy = 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 !== '') { + request.referrerPolicy = policy + } } // https://fetch.spec.whatwg.org/#cross-origin-resource-policy-check diff --git a/test/fetch/util.js b/test/fetch/util.js index 446ba82702b..5d6af5ce62b 100644 --- a/test/fetch/util.js +++ b/test/fetch/util.js @@ -135,7 +135,7 @@ test('isURLPotentiallyTrustworthy', (t) => { }) test('setRequestReferrerPolicyOnRedirect', nested => { - nested.plan(3) + nested.plan(7) nested.test('should set referrer policy from response headers on redirect', t => { const request = { @@ -156,6 +156,63 @@ test('setRequestReferrerPolicyOnRedirect', nested => { 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' @@ -192,6 +249,25 @@ test('setRequestReferrerPolicyOnRedirect', nested => { 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) => {