Add support for Referrer and Referrer Policy (#1057)
* Support referrer and referrerPolicy * Test TS types for addition of referrer and referrerPolicy * Fix lint issues and merge error
This commit is contained in:
parent
0a672754ce
commit
2d80b0bb3f
|
@ -71,6 +71,14 @@ export interface RequestInit {
|
|||
* An AbortSignal to set request's signal.
|
||||
*/
|
||||
signal?: AbortSignal | null;
|
||||
/**
|
||||
* A string whose value is a same-origin URL, "about:client", or the empty string, to set request’s referrer.
|
||||
*/
|
||||
referrer?: string;
|
||||
/**
|
||||
* A referrer policy to set request’s referrerPolicy.
|
||||
*/
|
||||
referrerPolicy?: ReferrerPolicy;
|
||||
|
||||
// Node-fetch extensions to the whatwg/fetch spec
|
||||
agent?: Agent | ((parsedUrl: URL) => Agent);
|
||||
|
@ -118,6 +126,7 @@ declare class BodyMixin {
|
|||
export interface Body extends Pick<BodyMixin, keyof BodyMixin> {}
|
||||
|
||||
export type RequestRedirect = 'error' | 'follow' | 'manual';
|
||||
export type ReferrerPolicy = '' | 'no-referrer' | 'no-referrer-when-downgrade' | 'same-origin' | 'origin' | 'strict-origin' | 'origin-when-cross-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
|
||||
export type RequestInfo = string | Request;
|
||||
export class Request extends BodyMixin {
|
||||
constructor(input: RequestInfo, init?: RequestInit);
|
||||
|
@ -142,6 +151,14 @@ export class Request extends BodyMixin {
|
|||
* Returns the URL of request as a string.
|
||||
*/
|
||||
readonly url: string;
|
||||
/**
|
||||
* A string whose value is a same-origin URL, "about:client", or the empty string, to set request’s referrer.
|
||||
*/
|
||||
readonly referrer: string;
|
||||
/**
|
||||
* A referrer policy to set request’s referrerPolicy.
|
||||
*/
|
||||
readonly referrerPolicy: ReferrerPolicy;
|
||||
clone(): Request;
|
||||
}
|
||||
|
||||
|
|
|
@ -581,8 +581,6 @@ Due to the nature of Node.js, the following properties are not implemented at th
|
|||
|
||||
- `type`
|
||||
- `destination`
|
||||
- `referrer`
|
||||
- `referrerPolicy`
|
||||
- `mode`
|
||||
- `credentials`
|
||||
- `cache`
|
||||
|
|
11
src/index.js
11
src/index.js
|
@ -19,6 +19,7 @@ import Request, {getNodeRequestOptions} from './request.js';
|
|||
import {FetchError} from './errors/fetch-error.js';
|
||||
import {AbortError} from './errors/abort-error.js';
|
||||
import {isRedirect} from './utils/is-redirect.js';
|
||||
import {parseReferrerPolicyFromHeader} from './utils/referrer.js';
|
||||
|
||||
export {Headers, Request, Response, FetchError, AbortError, isRedirect};
|
||||
|
||||
|
@ -168,7 +169,9 @@ export default async function fetch(url, options_) {
|
|||
method: request.method,
|
||||
body: clone(request),
|
||||
signal: request.signal,
|
||||
size: request.size
|
||||
size: request.size,
|
||||
referrer: request.referrer,
|
||||
referrerPolicy: request.referrerPolicy
|
||||
};
|
||||
|
||||
// HTTP-redirect fetch step 9
|
||||
|
@ -185,6 +188,12 @@ export default async function fetch(url, options_) {
|
|||
requestOptions.headers.delete('content-length');
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 14
|
||||
const responseReferrerPolicy = parseReferrerPolicyFromHeader(headers);
|
||||
if (responseReferrerPolicy) {
|
||||
requestOptions.referrerPolicy = responseReferrerPolicy;
|
||||
}
|
||||
|
||||
// HTTP-redirect fetch step 15
|
||||
resolve(fetch(new Request(locationURL, requestOptions)));
|
||||
finalize();
|
||||
|
|
|
@ -12,6 +12,9 @@ import Headers from './headers.js';
|
|||
import Body, {clone, extractContentType, getTotalBytes} from './body.js';
|
||||
import {isAbortSignal} from './utils/is.js';
|
||||
import {getSearch} from './utils/get-search.js';
|
||||
import {
|
||||
validateReferrerPolicy, determineRequestsReferrer, DEFAULT_REFERRER_POLICY
|
||||
} from './utils/referrer.js';
|
||||
|
||||
const INTERNALS = Symbol('Request internals');
|
||||
|
||||
|
@ -93,12 +96,28 @@ export default class Request extends Body {
|
|||
throw new TypeError('Expected signal to be an instanceof AbortSignal or EventTarget');
|
||||
}
|
||||
|
||||
// §5.4, Request constructor steps, step 15.1
|
||||
// eslint-disable-next-line no-eq-null, eqeqeq
|
||||
let referrer = init.referrer == null ? input.referrer : init.referrer;
|
||||
if (referrer === '') {
|
||||
// §5.4, Request constructor steps, step 15.2
|
||||
referrer = 'no-referrer';
|
||||
} else if (referrer) {
|
||||
// §5.4, Request constructor steps, step 15.3.1, 15.3.2
|
||||
const parsedReferrer = new URL(referrer);
|
||||
// §5.4, Request constructor steps, step 15.3.3, 15.3.4
|
||||
referrer = /^about:(\/\/)?client$/.test(parsedReferrer) ? 'client' : parsedReferrer;
|
||||
} else {
|
||||
referrer = undefined;
|
||||
}
|
||||
|
||||
this[INTERNALS] = {
|
||||
method,
|
||||
redirect: init.redirect || input.redirect || 'follow',
|
||||
headers,
|
||||
parsedURL,
|
||||
signal
|
||||
signal,
|
||||
referrer
|
||||
};
|
||||
|
||||
// Node-fetch-only options
|
||||
|
@ -108,6 +127,10 @@ export default class Request extends Body {
|
|||
this.agent = init.agent || input.agent;
|
||||
this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384;
|
||||
this.insecureHTTPParser = init.insecureHTTPParser || input.insecureHTTPParser || false;
|
||||
|
||||
// §5.4, Request constructor steps, step 16.
|
||||
// Default is empty string per https://fetch.spec.whatwg.org/#concept-request-referrer-policy
|
||||
this.referrerPolicy = init.referrerPolicy || input.referrerPolicy || '';
|
||||
}
|
||||
|
||||
get method() {
|
||||
|
@ -130,6 +153,31 @@ export default class Request extends Body {
|
|||
return this[INTERNALS].signal;
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#dom-request-referrer
|
||||
get referrer() {
|
||||
if (this[INTERNALS].referrer === 'no-referrer') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (this[INTERNALS].referrer === 'client') {
|
||||
return 'about:client';
|
||||
}
|
||||
|
||||
if (this[INTERNALS].referrer) {
|
||||
return this[INTERNALS].referrer.toString();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get referrerPolicy() {
|
||||
return this[INTERNALS].referrerPolicy;
|
||||
}
|
||||
|
||||
set referrerPolicy(referrerPolicy) {
|
||||
this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this request
|
||||
*
|
||||
|
@ -150,7 +198,9 @@ Object.defineProperties(Request.prototype, {
|
|||
headers: {enumerable: true},
|
||||
redirect: {enumerable: true},
|
||||
clone: {enumerable: true},
|
||||
signal: {enumerable: true}
|
||||
signal: {enumerable: true},
|
||||
referrer: {enumerable: true},
|
||||
referrerPolicy: {enumerable: true}
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -186,6 +236,29 @@ export const getNodeRequestOptions = request => {
|
|||
headers.set('Content-Length', contentLengthValue);
|
||||
}
|
||||
|
||||
// 4.1. Main fetch, step 2.6
|
||||
// > If request's referrer policy is the empty string, then set request's referrer policy to the
|
||||
// > default referrer policy.
|
||||
if (request.referrerPolicy === '') {
|
||||
request.referrerPolicy = DEFAULT_REFERRER_POLICY;
|
||||
}
|
||||
|
||||
// 4.1. Main fetch, step 2.7
|
||||
// > If request's referrer is not "no-referrer", set request's referrer to the result of invoking
|
||||
// > determine request's referrer.
|
||||
if (request.referrer && request.referrer !== 'no-referrer') {
|
||||
request[INTERNALS].referrer = determineRequestsReferrer(request);
|
||||
} else {
|
||||
request[INTERNALS].referrer = 'no-referrer';
|
||||
}
|
||||
|
||||
// 4.5. HTTP-network-or-cache fetch, step 6.9
|
||||
// > If httpRequest's referrer is a URL, then append `Referer`/httpRequest's referrer, serialized
|
||||
// > and isomorphic encoded, to httpRequest's header list.
|
||||
if (request[INTERNALS].referrer instanceof URL) {
|
||||
headers.set('Referer', request.referrer);
|
||||
}
|
||||
|
||||
// HTTP-network-or-cache fetch step 2.11
|
||||
if (!headers.has('User-Agent')) {
|
||||
headers.set('User-Agent', 'node-fetch');
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
import {isIP} from 'net';
|
||||
|
||||
/**
|
||||
* @external URL
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URL|URL}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module utils/referrer
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#strip-url|Referrer Policy §8.4. Strip url for use as a referrer}
|
||||
* @param {string} URL
|
||||
* @param {boolean} [originOnly=false]
|
||||
*/
|
||||
export function stripURLForUseAsAReferrer(url, originOnly = false) {
|
||||
// 1. If url is null, return no referrer.
|
||||
if (url == null) { // eslint-disable-line no-eq-null, eqeqeq
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
url = new URL(url);
|
||||
|
||||
// 2. If url's scheme is a local scheme, then return no referrer.
|
||||
if (/^(about|blob|data):$/.test(url.protocol)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 3. Set url's username to the empty string.
|
||||
url.username = '';
|
||||
|
||||
// 4. Set url's password to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the password being `"null"`.
|
||||
url.password = '';
|
||||
|
||||
// 5. Set url's fragment to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the fragment being `"#null"`.
|
||||
url.hash = '';
|
||||
|
||||
// 6. If the origin-only flag is true, then:
|
||||
if (originOnly) {
|
||||
// 6.1. Set url's path to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the path being `"/null"`.
|
||||
url.pathname = '';
|
||||
|
||||
// 6.2. Set url's query to null.
|
||||
// Note: `null` appears to be a mistake as this actually results in the query being `"?null"`.
|
||||
url.search = '';
|
||||
}
|
||||
|
||||
// 7. Return url.
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#enumdef-referrerpolicy|enum ReferrerPolicy}
|
||||
*/
|
||||
export const ReferrerPolicy = new Set([
|
||||
'',
|
||||
'no-referrer',
|
||||
'no-referrer-when-downgrade',
|
||||
'same-origin',
|
||||
'origin',
|
||||
'strict-origin',
|
||||
'origin-when-cross-origin',
|
||||
'strict-origin-when-cross-origin',
|
||||
'unsafe-url'
|
||||
]);
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#default-referrer-policy|default referrer policy}
|
||||
*/
|
||||
export const DEFAULT_REFERRER_POLICY = 'strict-origin-when-cross-origin';
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#referrer-policies|Referrer Policy §3. Referrer Policies}
|
||||
* @param {string} referrerPolicy
|
||||
* @returns {string} referrerPolicy
|
||||
*/
|
||||
export function validateReferrerPolicy(referrerPolicy) {
|
||||
if (!ReferrerPolicy.has(referrerPolicy)) {
|
||||
throw new TypeError(`Invalid referrerPolicy: ${referrerPolicy}`);
|
||||
}
|
||||
|
||||
return referrerPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy|Referrer Policy §3.2. Is origin potentially trustworthy?}
|
||||
* @param {external:URL} url
|
||||
* @returns `true`: "Potentially Trustworthy", `false`: "Not Trustworthy"
|
||||
*/
|
||||
export function isOriginPotentiallyTrustworthy(url) {
|
||||
// 1. If origin is an opaque origin, return "Not Trustworthy".
|
||||
// Not applicable
|
||||
|
||||
// 2. Assert: origin is a tuple origin.
|
||||
// Not for implementations
|
||||
|
||||
// 3. If origin's scheme is either "https" or "wss", return "Potentially Trustworthy".
|
||||
if (/^(http|ws)s:$/.test(url.protocol)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. If origin's host component matches one of the CIDR notations 127.0.0.0/8 or ::1/128 [RFC4632], return "Potentially Trustworthy".
|
||||
const hostIp = url.host.replace(/(^\[)|(]$)/g, '');
|
||||
const hostIPVersion = isIP(hostIp);
|
||||
|
||||
if (hostIPVersion === 4 && /^127\./.test(hostIp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hostIPVersion === 6 && /^(((0+:){7})|(::(0+:){0,6}))0*1$/.test(hostIp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5. If origin's host component is "localhost" or falls within ".localhost", and the user agent conforms to the name resolution rules in [let-localhost-be-localhost], return "Potentially Trustworthy".
|
||||
// We are returning FALSE here because we cannot ensure conformance to
|
||||
// let-localhost-be-loalhost (https://tools.ietf.org/html/draft-west-let-localhost-be-localhost)
|
||||
if (/^(.+\.)*localhost$/.test(url.host)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 6. If origin's scheme component is file, return "Potentially Trustworthy".
|
||||
if (url.protocol === 'file:') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 7. If origin's scheme component is one which the user agent considers to be authenticated, return "Potentially Trustworthy".
|
||||
// Not supported
|
||||
|
||||
// 8. If origin has been configured as a trustworthy origin, return "Potentially Trustworthy".
|
||||
// Not supported
|
||||
|
||||
// 9. Return "Not Trustworthy".
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-secure-contexts/#is-url-trustworthy|Referrer Policy §3.3. Is url potentially trustworthy?}
|
||||
* @param {external:URL} url
|
||||
* @returns `true`: "Potentially Trustworthy", `false`: "Not Trustworthy"
|
||||
*/
|
||||
export function isUrlPotentiallyTrustworthy(url) {
|
||||
// 1. If url is "about:blank" or "about:srcdoc", return "Potentially Trustworthy".
|
||||
if (/^about:(blank|srcdoc)$/.test(url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. If url's scheme is "data", return "Potentially Trustworthy".
|
||||
if (url.protocol === 'data:') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note: The origin of blob: and filesystem: URLs is the origin of the context in which they were
|
||||
// created. Therefore, blobs created in a trustworthy origin will themselves be potentially
|
||||
// trustworthy.
|
||||
if (/^(blob|filesystem):$/.test(url.protocol)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Return the result of executing §3.2 Is origin potentially trustworthy? on url's origin.
|
||||
return isOriginPotentiallyTrustworthy(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the referrerURL to enforce any extra security policy considerations.
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}, step 7
|
||||
* @callback module:utils/referrer~referrerURLCallback
|
||||
* @param {external:URL} referrerURL
|
||||
* @returns {external:URL} modified referrerURL
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modifies the referrerOrigin to enforce any extra security policy considerations.
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}, step 7
|
||||
* @callback module:utils/referrer~referrerOriginCallback
|
||||
* @param {external:URL} referrerOrigin
|
||||
* @returns {external:URL} modified referrerOrigin
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}
|
||||
* @param {Request} request
|
||||
* @param {object} o
|
||||
* @param {module:utils/referrer~referrerURLCallback} o.referrerURLCallback
|
||||
* @param {module:utils/referrer~referrerOriginCallback} o.referrerOriginCallback
|
||||
* @returns {external:URL} Request's referrer
|
||||
*/
|
||||
export function determineRequestsReferrer(request, {referrerURLCallback, referrerOriginCallback} = {}) {
|
||||
// There are 2 notes in the specification about invalid pre-conditions. We return null, here, for
|
||||
// these cases:
|
||||
// > Note: If request's referrer is "no-referrer", Fetch will not call into this algorithm.
|
||||
// > Note: If request's referrer policy is the empty string, Fetch will not call into this
|
||||
// > algorithm.
|
||||
if (request.referrer === 'no-referrer' || request.referrerPolicy === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. Let policy be request's associated referrer policy.
|
||||
const policy = request.referrerPolicy;
|
||||
|
||||
// 2. Let environment be request's client.
|
||||
// not applicable to node.js
|
||||
|
||||
// 3. Switch on request's referrer:
|
||||
if (request.referrer === 'about:client') {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// "a URL": Let referrerSource be request's referrer.
|
||||
const referrerSource = request.referrer;
|
||||
|
||||
// 4. Let request's referrerURL be the result of stripping referrerSource for use as a referrer.
|
||||
let referrerURL = stripURLForUseAsAReferrer(referrerSource);
|
||||
|
||||
// 5. Let referrerOrigin be the result of stripping referrerSource for use as a referrer, with the
|
||||
// origin-only flag set to true.
|
||||
let referrerOrigin = stripURLForUseAsAReferrer(referrerSource, true);
|
||||
|
||||
// 6. If the result of serializing referrerURL is a string whose length is greater than 4096, set
|
||||
// referrerURL to referrerOrigin.
|
||||
if (referrerURL.toString().length > 4096) {
|
||||
referrerURL = referrerOrigin;
|
||||
}
|
||||
|
||||
// 7. The user agent MAY alter referrerURL or referrerOrigin at this point to enforce arbitrary
|
||||
// policy considerations in the interests of minimizing data leakage. For example, the user
|
||||
// agent could strip the URL down to an origin, modify its host, replace it with an empty
|
||||
// string, etc.
|
||||
if (referrerURLCallback) {
|
||||
referrerURL = referrerURLCallback(referrerURL);
|
||||
}
|
||||
|
||||
if (referrerOriginCallback) {
|
||||
referrerOrigin = referrerOriginCallback(referrerOrigin);
|
||||
}
|
||||
|
||||
// 8.Execute the statements corresponding to the value of policy:
|
||||
const currentURL = new URL(request.url);
|
||||
|
||||
switch (policy) {
|
||||
case 'no-referrer':
|
||||
return 'no-referrer';
|
||||
|
||||
case 'origin':
|
||||
return referrerOrigin;
|
||||
|
||||
case 'unsafe-url':
|
||||
return referrerURL;
|
||||
|
||||
case 'strict-origin':
|
||||
// 1. If referrerURL is a potentially trustworthy URL and request's current URL is not a
|
||||
// potentially trustworthy URL, then return no referrer.
|
||||
if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 2. Return referrerOrigin.
|
||||
return referrerOrigin.toString();
|
||||
|
||||
case 'strict-origin-when-cross-origin':
|
||||
// 1. If the origin of referrerURL and the origin of request's current URL are the same, then
|
||||
// return referrerURL.
|
||||
if (referrerURL.origin === currentURL.origin) {
|
||||
return referrerURL;
|
||||
}
|
||||
|
||||
// 2. If referrerURL is a potentially trustworthy URL and request's current URL is not a
|
||||
// potentially trustworthy URL, then return no referrer.
|
||||
if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 3. Return referrerOrigin.
|
||||
return referrerOrigin;
|
||||
|
||||
case 'same-origin':
|
||||
// 1. If the origin of referrerURL and the origin of request's current URL are the same, then
|
||||
// return referrerURL.
|
||||
if (referrerURL.origin === currentURL.origin) {
|
||||
return referrerURL;
|
||||
}
|
||||
|
||||
// 2. Return no referrer.
|
||||
return 'no-referrer';
|
||||
|
||||
case 'origin-when-cross-origin':
|
||||
// 1. If the origin of referrerURL and the origin of request's current URL are the same, then
|
||||
// return referrerURL.
|
||||
if (referrerURL.origin === currentURL.origin) {
|
||||
return referrerURL;
|
||||
}
|
||||
|
||||
// Return referrerOrigin.
|
||||
return referrerOrigin;
|
||||
|
||||
case 'no-referrer-when-downgrade':
|
||||
// 1. If referrerURL is a potentially trustworthy URL and request's current URL is not a
|
||||
// potentially trustworthy URL, then return no referrer.
|
||||
if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) {
|
||||
return 'no-referrer';
|
||||
}
|
||||
|
||||
// 2. Return referrerURL.
|
||||
return referrerURL;
|
||||
|
||||
default:
|
||||
throw new TypeError(`Invalid referrerPolicy: ${policy}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header|Referrer Policy §8.1. Parse a referrer policy from a Referrer-Policy header}
|
||||
* @param {Headers} headers Response headers
|
||||
* @returns {string} policy
|
||||
*/
|
||||
export function parseReferrerPolicyFromHeader(headers) {
|
||||
// 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy`
|
||||
// and response’s header list.
|
||||
const policyTokens = (headers.get('referrer-policy') || '').split(/[,\s]+/);
|
||||
|
||||
// 2. Let policy be the empty string.
|
||||
let policy = '';
|
||||
|
||||
// 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.
|
||||
// Note: This algorithm loops over multiple policy values to allow deployment of new policy
|
||||
// values with fallbacks for older user agents, as described in § 11.1 Unknown Policy Values.
|
||||
for (const token of policyTokens) {
|
||||
if (token && ReferrerPolicy.has(token)) {
|
||||
policy = token;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return policy.
|
||||
return policy;
|
||||
}
|
|
@ -0,0 +1,552 @@
|
|||
import chai from 'chai';
|
||||
|
||||
import fetch, {Request, Headers} from '../src/index.js';
|
||||
import {
|
||||
DEFAULT_REFERRER_POLICY, ReferrerPolicy, stripURLForUseAsAReferrer, validateReferrerPolicy,
|
||||
isOriginPotentiallyTrustworthy, isUrlPotentiallyTrustworthy, determineRequestsReferrer,
|
||||
parseReferrerPolicyFromHeader
|
||||
} from '../src/utils/referrer.js';
|
||||
import TestServer from './utils/server.js';
|
||||
|
||||
const {expect} = chai;
|
||||
|
||||
describe('fetch() with referrer and referrerPolicy', () => {
|
||||
const local = new TestServer();
|
||||
let base;
|
||||
|
||||
before(async () => {
|
||||
await local.start();
|
||||
base = `http://${local.hostname}:${local.port}/`;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
return local.stop();
|
||||
});
|
||||
|
||||
it('should send request without a referrer by default', () => {
|
||||
return fetch(`${base}inspect`).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
it('should send request with a referrer', () => {
|
||||
return fetch(`${base}inspect`, {
|
||||
referrer: base,
|
||||
referrerPolicy: 'unsafe-url'
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.equal(base);
|
||||
});
|
||||
});
|
||||
|
||||
it('should send request with referrerPolicy strict-origin-when-cross-origin by default', () => {
|
||||
return Promise.all([
|
||||
fetch(`${base}inspect`, {
|
||||
referrer: base
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.equal(base);
|
||||
}),
|
||||
fetch(`${base}inspect`, {
|
||||
referrer: 'https://example.com'
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.be.undefined;
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('should send request with a referrer and respect redirected referrer-policy', () => {
|
||||
return Promise.all([
|
||||
fetch(`${base}redirect/referrer-policy`, {
|
||||
referrer: base
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.equal(base);
|
||||
}),
|
||||
fetch(`${base}redirect/referrer-policy`, {
|
||||
referrer: 'https://example.com'
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.be.undefined;
|
||||
}),
|
||||
fetch(`${base}redirect/referrer-policy`, {
|
||||
referrer: 'https://example.com',
|
||||
referrerPolicy: 'unsafe-url'
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.equal('https://example.com/');
|
||||
}),
|
||||
fetch(`${base}redirect/referrer-policy/same-origin`, {
|
||||
referrer: 'https://example.com',
|
||||
referrerPolicy: 'unsafe-url'
|
||||
}).then(res => res.json()).then(res => {
|
||||
expect(res.headers.referer).to.undefined;
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Request constructor', () => {
|
||||
describe('referrer', () => {
|
||||
it('should leave referrer undefined by default', () => {
|
||||
const req = new Request('http://example.com');
|
||||
expect(req.referrer).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should accept empty string referrer as no-referrer', () => {
|
||||
const referrer = '';
|
||||
const req = new Request('http://example.com', {referrer});
|
||||
expect(req.referrer).to.equal(referrer);
|
||||
});
|
||||
|
||||
it('should accept about:client referrer as client', () => {
|
||||
const referrer = 'about:client';
|
||||
const req = new Request('http://example.com', {referrer});
|
||||
expect(req.referrer).to.equal(referrer);
|
||||
});
|
||||
|
||||
it('should accept about://client referrer as client', () => {
|
||||
const req = new Request('http://example.com', {referrer: 'about://client'});
|
||||
expect(req.referrer).to.equal('about:client');
|
||||
});
|
||||
|
||||
it('should accept a string URL referrer', () => {
|
||||
const referrer = 'http://example.com/';
|
||||
const req = new Request('http://example.com', {referrer});
|
||||
expect(req.referrer).to.equal(referrer);
|
||||
});
|
||||
|
||||
it('should accept a URL referrer', () => {
|
||||
const referrer = new URL('http://example.com');
|
||||
const req = new Request('http://example.com', {referrer});
|
||||
expect(req.referrer).to.equal(referrer.toString());
|
||||
});
|
||||
|
||||
it('should accept a referrer from input', () => {
|
||||
const referrer = 'http://example.com/';
|
||||
const req = new Request(new Request('http://example.com', {referrer}));
|
||||
expect(req.referrer).to.equal(referrer.toString());
|
||||
});
|
||||
|
||||
it('should throw a TypeError for an invalid URL', () => {
|
||||
expect(() => {
|
||||
const req = new Request('http://example.com', {referrer: 'foobar'});
|
||||
expect.fail(req);
|
||||
}).to.throw(TypeError, 'Invalid URL: foobar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('referrerPolicy', () => {
|
||||
it('should default refererPolicy to empty string', () => {
|
||||
const req = new Request('http://example.com');
|
||||
expect(req.referrerPolicy).to.equal('');
|
||||
});
|
||||
|
||||
it('should accept refererPolicy', () => {
|
||||
const referrerPolicy = 'unsafe-url';
|
||||
const req = new Request('http://example.com', {referrerPolicy});
|
||||
expect(req.referrerPolicy).to.equal(referrerPolicy);
|
||||
});
|
||||
|
||||
it('should accept referrerPolicy from input', () => {
|
||||
const referrerPolicy = 'unsafe-url';
|
||||
const req = new Request(new Request('http://example.com', {referrerPolicy}));
|
||||
expect(req.referrerPolicy).to.equal(referrerPolicy);
|
||||
});
|
||||
|
||||
it('should throw a TypeError for an invalid referrerPolicy', () => {
|
||||
expect(() => {
|
||||
const req = new Request('http://example.com', {referrerPolicy: 'foobar'});
|
||||
expect.fail(req);
|
||||
}).to.throw(TypeError, 'Invalid referrerPolicy: foobar');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('utils/referrer', () => {
|
||||
it('default policy should be strict-origin-when-cross-origin', () => {
|
||||
expect(DEFAULT_REFERRER_POLICY).to.equal('strict-origin-when-cross-origin');
|
||||
});
|
||||
|
||||
describe('stripURLForUseAsAReferrer', () => {
|
||||
it('should return no-referrer for null/undefined URL', () => {
|
||||
expect(stripURLForUseAsAReferrer(undefined)).to.equal('no-referrer');
|
||||
expect(stripURLForUseAsAReferrer(null)).to.equal('no-referrer');
|
||||
});
|
||||
|
||||
it('should return no-referrer for about:, blob:, and data: URLs', () => {
|
||||
expect(stripURLForUseAsAReferrer('about:client')).to.equal('no-referrer');
|
||||
expect(stripURLForUseAsAReferrer('blob:theblog')).to.equal('no-referrer');
|
||||
expect(stripURLForUseAsAReferrer('data:,thedata')).to.equal('no-referrer');
|
||||
});
|
||||
|
||||
it('should strip the username, password, and hash', () => {
|
||||
const urlStr = 'http://foo:bar@example.com/foo?q=search#theanchor';
|
||||
expect(stripURLForUseAsAReferrer(urlStr).toString())
|
||||
.to.equal('http://example.com/foo?q=search');
|
||||
});
|
||||
|
||||
it('should strip the pathname and query when origin-only', () => {
|
||||
const urlStr = 'http://foo:bar@example.com/foo?q=search#theanchor';
|
||||
expect(stripURLForUseAsAReferrer(urlStr, true).toString())
|
||||
.to.equal('http://example.com/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateReferrerPolicy', () => {
|
||||
it('should return the referrer policy', () => {
|
||||
for (const referrerPolicy of ReferrerPolicy) {
|
||||
expect(validateReferrerPolicy(referrerPolicy)).to.equal(referrerPolicy);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw a TypeError for invalid referrer policies', () => {
|
||||
expect(validateReferrerPolicy.bind(null, undefined))
|
||||
.to.throw(TypeError, 'Invalid referrerPolicy: undefined');
|
||||
expect(validateReferrerPolicy.bind(null, null))
|
||||
.to.throw(TypeError, 'Invalid referrerPolicy: null');
|
||||
expect(validateReferrerPolicy.bind(null, false))
|
||||
.to.throw(TypeError, 'Invalid referrerPolicy: false');
|
||||
expect(validateReferrerPolicy.bind(null, 0))
|
||||
.to.throw(TypeError, 'Invalid referrerPolicy: 0');
|
||||
expect(validateReferrerPolicy.bind(null, 'always'))
|
||||
.to.throw(TypeError, 'Invalid referrerPolicy: always');
|
||||
});
|
||||
});
|
||||
|
||||
const testIsOriginPotentiallyTrustworthyStatements = func => {
|
||||
it('should be potentially trustworthy for HTTPS and WSS URLs', () => {
|
||||
expect(func(new URL('https://example.com'))).to.be.true;
|
||||
expect(func(new URL('wss://example.com'))).to.be.true;
|
||||
});
|
||||
|
||||
it('should be potentially trustworthy for loopback IP address URLs', () => {
|
||||
expect(func(new URL('http://127.0.0.1'))).to.be.true;
|
||||
expect(func(new URL('http://127.1.2.3'))).to.be.true;
|
||||
expect(func(new URL('ws://[::1]'))).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be potentially trustworthy for "localhost" URLs', () => {
|
||||
expect(func(new URL('http://localhost'))).to.be.false;
|
||||
});
|
||||
|
||||
it('should be potentially trustworthy for file: URLs', () => {
|
||||
expect(func(new URL('file://foo/bar'))).to.be.true;
|
||||
});
|
||||
|
||||
it('should not be potentially trustworthy for all other origins', () => {
|
||||
expect(func(new URL('http://example.com'))).to.be.false;
|
||||
expect(func(new URL('ws://example.com'))).to.be.false;
|
||||
});
|
||||
};
|
||||
|
||||
describe('isOriginPotentiallyTrustworthy', () => {
|
||||
testIsOriginPotentiallyTrustworthyStatements(isOriginPotentiallyTrustworthy);
|
||||
});
|
||||
|
||||
describe('isUrlPotentiallyTrustworthy', () => {
|
||||
it('should be potentially trustworthy for about:blank and about:srcdoc', () => {
|
||||
expect(isUrlPotentiallyTrustworthy(new URL('about:blank'))).to.be.true;
|
||||
expect(isUrlPotentiallyTrustworthy(new URL('about:srcdoc'))).to.be.true;
|
||||
});
|
||||
|
||||
it('should be potentially trustworthy for data: URLs', () => {
|
||||
expect(isUrlPotentiallyTrustworthy(new URL('data:,thedata'))).to.be.true;
|
||||
});
|
||||
|
||||
it('should be potentially trustworthy for blob: and filesystem: URLs', () => {
|
||||
expect(isUrlPotentiallyTrustworthy(new URL('blob:theblob'))).to.be.true;
|
||||
expect(isUrlPotentiallyTrustworthy(new URL('filesystem:thefilesystem'))).to.be.true;
|
||||
});
|
||||
|
||||
testIsOriginPotentiallyTrustworthyStatements(isUrlPotentiallyTrustworthy);
|
||||
});
|
||||
|
||||
describe('determineRequestsReferrer', () => {
|
||||
it('should return null for no-referrer or empty referrerPolicy', () => {
|
||||
expect(determineRequestsReferrer({referrer: 'no-referrer'})).to.be.null;
|
||||
expect(determineRequestsReferrer({referrerPolicy: ''})).to.be.null;
|
||||
});
|
||||
|
||||
it('should return no-referrer for about:client', () => {
|
||||
expect(determineRequestsReferrer({
|
||||
referrer: 'about:client',
|
||||
referrerPolicy: DEFAULT_REFERRER_POLICY
|
||||
})).to.equal('no-referrer');
|
||||
});
|
||||
|
||||
it('should return just the origin for URLs over 4096 characters', () => {
|
||||
expect(determineRequestsReferrer({
|
||||
url: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrer: `http://example.com/${'0'.repeat(4096)}`,
|
||||
referrerPolicy: DEFAULT_REFERRER_POLICY
|
||||
}).toString()).to.equal('http://example.com/');
|
||||
});
|
||||
|
||||
it('should alter the referrer URL by callback', () => {
|
||||
expect(determineRequestsReferrer({
|
||||
url: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrer: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrerPolicy: 'unsafe-url'
|
||||
}, {
|
||||
referrerURLCallback: referrerURL => {
|
||||
return new URL(referrerURL.toString().replace(/^http:/, 'myprotocol:'));
|
||||
}
|
||||
}).toString()).to.equal('myprotocol://example.com/foo?q=search');
|
||||
});
|
||||
|
||||
it('should alter the referrer origin by callback', () => {
|
||||
expect(determineRequestsReferrer({
|
||||
url: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrer: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrerPolicy: 'origin'
|
||||
}, {
|
||||
referrerOriginCallback: referrerOrigin => {
|
||||
return new URL(referrerOrigin.toString().replace(/^http:/, 'myprotocol:'));
|
||||
}
|
||||
}).toString()).to.equal('myprotocol://example.com/');
|
||||
});
|
||||
|
||||
it('should throw a TypeError for an invalid policy', () => {
|
||||
expect(() => {
|
||||
determineRequestsReferrer({
|
||||
url: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrer: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrerPolicy: 'always'
|
||||
});
|
||||
}).to.throw(TypeError, 'Invalid referrerPolicy: always');
|
||||
});
|
||||
|
||||
const referrerPolicyTestLabel = ({currentURLTrust, referrerURLTrust, sameOrigin}) => {
|
||||
if (currentURLTrust === null && referrerURLTrust === null && sameOrigin === null) {
|
||||
return 'Always';
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
if (currentURLTrust !== null) {
|
||||
result.push(`Current URL is ${currentURLTrust ? '' : 'not '}potentially trustworthy`);
|
||||
}
|
||||
|
||||
if (referrerURLTrust !== null) {
|
||||
result.push(`Referrer URL is ${referrerURLTrust ? '' : 'not '}potentially trustworthy`);
|
||||
}
|
||||
|
||||
if (sameOrigin !== null) {
|
||||
result.push(`Current URL & Referrer URL do ${sameOrigin ? '' : 'not '}have same origin`);
|
||||
}
|
||||
|
||||
return result.join(', ');
|
||||
};
|
||||
|
||||
const referrerPolicyTests = (referrerPolicy, matrix) => {
|
||||
describe(`Referrer policy: ${referrerPolicy}`, () => {
|
||||
for (const {currentURLTrust, referrerURLTrust, sameOrigin, result} of matrix) {
|
||||
describe(referrerPolicyTestLabel({currentURLTrust, referrerURLTrust, sameOrigin}), () => {
|
||||
const requests = [];
|
||||
|
||||
if (sameOrigin === true || sameOrigin === null) {
|
||||
requests.push({
|
||||
referrerPolicy,
|
||||
url: 'http://foo:bar@example.com/foo?q=search#theanchor',
|
||||
referrer: 'http://foo:bar@example.com/foo?q=search#theanchor'
|
||||
});
|
||||
}
|
||||
|
||||
if (sameOrigin === false || sameOrigin === null) {
|
||||
requests.push({
|
||||
referrerPolicy,
|
||||
url: 'http://foo:bar@example2.com/foo?q=search#theanchor',
|
||||
referrer: 'http://foo:bar@example.com/foo?q=search#theanchor'
|
||||
});
|
||||
}
|
||||
|
||||
let requestsLength = requests.length;
|
||||
switch (currentURLTrust) {
|
||||
case null:
|
||||
for (let i = 0; i < requestsLength; i++) {
|
||||
const req = requests[i];
|
||||
requests.push({...req, url: req.url.replace(/^http:/, 'https:')});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case true:
|
||||
for (let i = 0; i < requestsLength; i++) {
|
||||
const req = requests[i];
|
||||
req.url = req.url.replace(/^http:/, 'https:');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
// nothing to do, default is not potentially trustworthy
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new TypeError(`Invalid currentURLTrust condition: ${currentURLTrust}`);
|
||||
}
|
||||
|
||||
requestsLength = requests.length;
|
||||
switch (referrerURLTrust) {
|
||||
case null:
|
||||
for (let i = 0; i < requestsLength; i++) {
|
||||
const req = requests[i];
|
||||
|
||||
if (sameOrigin) {
|
||||
if (req.url.startsWith('https:')) {
|
||||
requests.splice(i, 1);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
requests.push({...req, referrer: req.referrer.replace(/^http:/, 'https:')});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case true:
|
||||
for (let i = 0; i < requestsLength; i++) {
|
||||
const req = requests[i];
|
||||
req.referrer = req.referrer.replace(/^http:/, 'https:');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case false:
|
||||
// nothing to do, default is not potentially trustworthy
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new TypeError(`Invalid referrerURLTrust condition: ${referrerURLTrust}`);
|
||||
}
|
||||
|
||||
it('should have tests', () => {
|
||||
expect(requests).to.not.be.empty;
|
||||
});
|
||||
|
||||
for (const req of requests) {
|
||||
it(`should return ${result} for url: ${req.url}, referrer: ${req.referrer}`, () => {
|
||||
if (result === 'no-referrer') {
|
||||
return expect(determineRequestsReferrer(req).toString())
|
||||
.to.equal('no-referrer');
|
||||
}
|
||||
|
||||
if (result === 'referrer-origin') {
|
||||
const referrerOrigih = stripURLForUseAsAReferrer(req.referrer, true);
|
||||
return expect(determineRequestsReferrer(req).toString())
|
||||
.to.equal(referrerOrigih.toString());
|
||||
}
|
||||
|
||||
if (result === 'referrer-url') {
|
||||
const referrerURL = stripURLForUseAsAReferrer(req.referrer);
|
||||
return expect(determineRequestsReferrer(req).toString())
|
||||
.to.equal(referrerURL.toString());
|
||||
}
|
||||
|
||||
throw new TypeError(`Invalid result: ${result}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 3.1 no-referrer
|
||||
referrerPolicyTests('no-referrer', [
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: null, result: 'no-referrer'}
|
||||
]);
|
||||
|
||||
// 3.2 no-referrer-when-downgrade
|
||||
referrerPolicyTests('no-referrer-when-downgrade', [
|
||||
{currentURLTrust: false, referrerURLTrust: true, sameOrigin: null, result: 'no-referrer'},
|
||||
{currentURLTrust: null, referrerURLTrust: false, sameOrigin: null, result: 'referrer-url'},
|
||||
{currentURLTrust: true, referrerURLTrust: true, sameOrigin: null, result: 'referrer-url'}
|
||||
]);
|
||||
|
||||
// 3.3 same-origin
|
||||
referrerPolicyTests('same-origin', [
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: false, result: 'no-referrer'},
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: true, result: 'referrer-url'}
|
||||
]);
|
||||
|
||||
// 3.4 origin
|
||||
referrerPolicyTests('origin', [
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: null, result: 'referrer-origin'}
|
||||
]);
|
||||
|
||||
// 3.5 strict-origin
|
||||
referrerPolicyTests('strict-origin', [
|
||||
{currentURLTrust: false, referrerURLTrust: true, sameOrigin: null, result: 'no-referrer'},
|
||||
{currentURLTrust: null, referrerURLTrust: false, sameOrigin: null, result: 'referrer-origin'},
|
||||
{currentURLTrust: true, referrerURLTrust: true, sameOrigin: null, result: 'referrer-origin'}
|
||||
]);
|
||||
|
||||
// 3.6 origin-when-cross-origin
|
||||
referrerPolicyTests('origin-when-cross-origin', [
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: false, result: 'referrer-origin'},
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: true, result: 'referrer-url'}
|
||||
]);
|
||||
|
||||
// 3.7 strict-origin-when-cross-origin
|
||||
referrerPolicyTests('strict-origin-when-cross-origin', [
|
||||
{currentURLTrust: false, referrerURLTrust: true, sameOrigin: false, result: 'no-referrer'},
|
||||
{currentURLTrust: null, referrerURLTrust: false, sameOrigin: false,
|
||||
result: 'referrer-origin'},
|
||||
{currentURLTrust: true, referrerURLTrust: true, sameOrigin: false, result: 'referrer-origin'},
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: true, result: 'referrer-url'}
|
||||
]);
|
||||
|
||||
// 3.8 unsafe-url
|
||||
referrerPolicyTests('unsafe-url', [
|
||||
{currentURLTrust: null, referrerURLTrust: null, sameOrigin: null, result: 'referrer-url'}
|
||||
]);
|
||||
});
|
||||
|
||||
describe('parseReferrerPolicyFromHeader', () => {
|
||||
it('should return an empty string when no referrer policy is found', () => {
|
||||
expect(parseReferrerPolicyFromHeader(new Headers())).to.equal('');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([['Referrer-Policy', '']])
|
||||
)).to.equal('');
|
||||
});
|
||||
|
||||
it('should return the last valid referrer policy', () => {
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([['Referrer-Policy', 'no-referrer']])
|
||||
)).to.equal('no-referrer');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([['Referrer-Policy', 'no-referrer unsafe-url']])
|
||||
)).to.equal('unsafe-url');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([['Referrer-Policy', 'foo no-referrer bar']])
|
||||
)).to.equal('no-referrer');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([['Referrer-Policy', 'foo no-referrer unsafe-url bar']])
|
||||
)).to.equal('unsafe-url');
|
||||
});
|
||||
|
||||
it('should use all Referrer-Policy headers', () => {
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([
|
||||
['Referrer-Policy', 'no-referrer'],
|
||||
['Referrer-Policy', '']
|
||||
])
|
||||
)).to.equal('no-referrer');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([
|
||||
['Referrer-Policy', 'no-referrer'],
|
||||
['Referrer-Policy', 'unsafe-url']
|
||||
])
|
||||
)).to.equal('unsafe-url');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([
|
||||
['Referrer-Policy', 'no-referrer foo'],
|
||||
['Referrer-Policy', 'bar unsafe-url wow']
|
||||
])
|
||||
)).to.equal('unsafe-url');
|
||||
expect(parseReferrerPolicyFromHeader(
|
||||
new Headers([
|
||||
['Referrer-Policy', 'no-referrer unsafe-url'],
|
||||
['Referrer-Policy', 'foo bar']
|
||||
])
|
||||
)).to.equal('unsafe-url');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -301,6 +301,20 @@ export default class TestServer {
|
|||
res.socket.end('\r\n');
|
||||
}
|
||||
|
||||
if (p === '/redirect/referrer-policy') {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.setHeader('Referrer-Policy', 'foo unsafe-url bar');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/referrer-policy/same-origin') {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.setHeader('Referrer-Policy', 'foo unsafe-url same-origin bar');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/chunked') {
|
||||
res.writeHead(301, {
|
||||
Location: '/inspect',
|
||||
|
|
Loading…
Reference in New Issue