mirror of
https://github.com/oven-sh/setup-bun.git
synced 2025-07-19 21:18:22 +02:00
fix: use node fetch
This commit is contained in:
parent
8aad90088f
commit
6f1924d3c0
235 changed files with 59466 additions and 18798 deletions
9
node_modules/node-fetch/src/utils/get-search.js
generated
vendored
Normal file
9
node_modules/node-fetch/src/utils/get-search.js
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
export const getSearch = parsedURL => {
|
||||
if (parsedURL.search) {
|
||||
return parsedURL.search;
|
||||
}
|
||||
|
||||
const lastOffset = parsedURL.href.length - 1;
|
||||
const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : '');
|
||||
return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : '';
|
||||
};
|
11
node_modules/node-fetch/src/utils/is-redirect.js
generated
vendored
Normal file
11
node_modules/node-fetch/src/utils/is-redirect.js
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
const redirectStatus = new Set([301, 302, 303, 307, 308]);
|
||||
|
||||
/**
|
||||
* Redirect code matching
|
||||
*
|
||||
* @param {number} code - Status code
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isRedirect = code => {
|
||||
return redirectStatus.has(code);
|
||||
};
|
73
node_modules/node-fetch/src/utils/is.js
generated
vendored
Normal file
73
node_modules/node-fetch/src/utils/is.js
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* Is.js
|
||||
*
|
||||
* Object type checks.
|
||||
*/
|
||||
|
||||
const NAME = Symbol.toStringTag;
|
||||
|
||||
/**
|
||||
* Check if `obj` is a URLSearchParams object
|
||||
* ref: https://github.com/node-fetch/node-fetch/issues/296#issuecomment-307598143
|
||||
* @param {*} object - Object to check for
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isURLSearchParameters = object => {
|
||||
return (
|
||||
typeof object === 'object' &&
|
||||
typeof object.append === 'function' &&
|
||||
typeof object.delete === 'function' &&
|
||||
typeof object.get === 'function' &&
|
||||
typeof object.getAll === 'function' &&
|
||||
typeof object.has === 'function' &&
|
||||
typeof object.set === 'function' &&
|
||||
typeof object.sort === 'function' &&
|
||||
object[NAME] === 'URLSearchParams'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `object` is a W3C `Blob` object (which `File` inherits from)
|
||||
* @param {*} object - Object to check for
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isBlob = object => {
|
||||
return (
|
||||
object &&
|
||||
typeof object === 'object' &&
|
||||
typeof object.arrayBuffer === 'function' &&
|
||||
typeof object.type === 'string' &&
|
||||
typeof object.stream === 'function' &&
|
||||
typeof object.constructor === 'function' &&
|
||||
/^(Blob|File)$/.test(object[NAME])
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `obj` is an instance of AbortSignal.
|
||||
* @param {*} object - Object to check for
|
||||
* @return {boolean}
|
||||
*/
|
||||
export const isAbortSignal = object => {
|
||||
return (
|
||||
typeof object === 'object' && (
|
||||
object[NAME] === 'AbortSignal' ||
|
||||
object[NAME] === 'EventTarget'
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of
|
||||
* the parent domain.
|
||||
*
|
||||
* Both domains must already be in canonical form.
|
||||
* @param {string|URL} original
|
||||
* @param {string|URL} destination
|
||||
*/
|
||||
export const isDomainOrSubdomain = (destination, original) => {
|
||||
const orig = new URL(original).hostname;
|
||||
const dest = new URL(destination).hostname;
|
||||
|
||||
return orig === dest || orig.endsWith(`.${dest}`);
|
||||
};
|
432
node_modules/node-fetch/src/utils/multipart-parser.js
generated
vendored
Normal file
432
node_modules/node-fetch/src/utils/multipart-parser.js
generated
vendored
Normal file
|
@ -0,0 +1,432 @@
|
|||
import {File} from 'fetch-blob/from.js';
|
||||
import {FormData} from 'formdata-polyfill/esm.min.js';
|
||||
|
||||
let s = 0;
|
||||
const S = {
|
||||
START_BOUNDARY: s++,
|
||||
HEADER_FIELD_START: s++,
|
||||
HEADER_FIELD: s++,
|
||||
HEADER_VALUE_START: s++,
|
||||
HEADER_VALUE: s++,
|
||||
HEADER_VALUE_ALMOST_DONE: s++,
|
||||
HEADERS_ALMOST_DONE: s++,
|
||||
PART_DATA_START: s++,
|
||||
PART_DATA: s++,
|
||||
END: s++
|
||||
};
|
||||
|
||||
let f = 1;
|
||||
const F = {
|
||||
PART_BOUNDARY: f,
|
||||
LAST_BOUNDARY: f *= 2
|
||||
};
|
||||
|
||||
const LF = 10;
|
||||
const CR = 13;
|
||||
const SPACE = 32;
|
||||
const HYPHEN = 45;
|
||||
const COLON = 58;
|
||||
const A = 97;
|
||||
const Z = 122;
|
||||
|
||||
const lower = c => c | 0x20;
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
class MultipartParser {
|
||||
/**
|
||||
* @param {string} boundary
|
||||
*/
|
||||
constructor(boundary) {
|
||||
this.index = 0;
|
||||
this.flags = 0;
|
||||
|
||||
this.onHeaderEnd = noop;
|
||||
this.onHeaderField = noop;
|
||||
this.onHeadersEnd = noop;
|
||||
this.onHeaderValue = noop;
|
||||
this.onPartBegin = noop;
|
||||
this.onPartData = noop;
|
||||
this.onPartEnd = noop;
|
||||
|
||||
this.boundaryChars = {};
|
||||
|
||||
boundary = '\r\n--' + boundary;
|
||||
const ui8a = new Uint8Array(boundary.length);
|
||||
for (let i = 0; i < boundary.length; i++) {
|
||||
ui8a[i] = boundary.charCodeAt(i);
|
||||
this.boundaryChars[ui8a[i]] = true;
|
||||
}
|
||||
|
||||
this.boundary = ui8a;
|
||||
this.lookbehind = new Uint8Array(this.boundary.length + 8);
|
||||
this.state = S.START_BOUNDARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} data
|
||||
*/
|
||||
write(data) {
|
||||
let i = 0;
|
||||
const length_ = data.length;
|
||||
let previousIndex = this.index;
|
||||
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
|
||||
const boundaryLength = this.boundary.length;
|
||||
const boundaryEnd = boundaryLength - 1;
|
||||
const bufferLength = data.length;
|
||||
let c;
|
||||
let cl;
|
||||
|
||||
const mark = name => {
|
||||
this[name + 'Mark'] = i;
|
||||
};
|
||||
|
||||
const clear = name => {
|
||||
delete this[name + 'Mark'];
|
||||
};
|
||||
|
||||
const callback = (callbackSymbol, start, end, ui8a) => {
|
||||
if (start === undefined || start !== end) {
|
||||
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
|
||||
}
|
||||
};
|
||||
|
||||
const dataCallback = (name, clear) => {
|
||||
const markSymbol = name + 'Mark';
|
||||
if (!(markSymbol in this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
callback(name, this[markSymbol], i, data);
|
||||
delete this[markSymbol];
|
||||
} else {
|
||||
callback(name, this[markSymbol], data.length, data);
|
||||
this[markSymbol] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
for (i = 0; i < length_; i++) {
|
||||
c = data[i];
|
||||
|
||||
switch (state) {
|
||||
case S.START_BOUNDARY:
|
||||
if (index === boundary.length - 2) {
|
||||
if (c === HYPHEN) {
|
||||
flags |= F.LAST_BOUNDARY;
|
||||
} else if (c !== CR) {
|
||||
return;
|
||||
}
|
||||
|
||||
index++;
|
||||
break;
|
||||
} else if (index - 1 === boundary.length - 2) {
|
||||
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
|
||||
state = S.END;
|
||||
flags = 0;
|
||||
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
|
||||
index = 0;
|
||||
callback('onPartBegin');
|
||||
state = S.HEADER_FIELD_START;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c !== boundary[index + 2]) {
|
||||
index = -2;
|
||||
}
|
||||
|
||||
if (c === boundary[index + 2]) {
|
||||
index++;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.HEADER_FIELD_START:
|
||||
state = S.HEADER_FIELD;
|
||||
mark('onHeaderField');
|
||||
index = 0;
|
||||
// falls through
|
||||
case S.HEADER_FIELD:
|
||||
if (c === CR) {
|
||||
clear('onHeaderField');
|
||||
state = S.HEADERS_ALMOST_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (c === HYPHEN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (c === COLON) {
|
||||
if (index === 1) {
|
||||
// empty header field
|
||||
return;
|
||||
}
|
||||
|
||||
dataCallback('onHeaderField', true);
|
||||
state = S.HEADER_VALUE_START;
|
||||
break;
|
||||
}
|
||||
|
||||
cl = lower(c);
|
||||
if (cl < A || cl > Z) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.HEADER_VALUE_START:
|
||||
if (c === SPACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
mark('onHeaderValue');
|
||||
state = S.HEADER_VALUE;
|
||||
// falls through
|
||||
case S.HEADER_VALUE:
|
||||
if (c === CR) {
|
||||
dataCallback('onHeaderValue', true);
|
||||
callback('onHeaderEnd');
|
||||
state = S.HEADER_VALUE_ALMOST_DONE;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.HEADER_VALUE_ALMOST_DONE:
|
||||
if (c !== LF) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
case S.HEADERS_ALMOST_DONE:
|
||||
if (c !== LF) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback('onHeadersEnd');
|
||||
state = S.PART_DATA_START;
|
||||
break;
|
||||
case S.PART_DATA_START:
|
||||
state = S.PART_DATA;
|
||||
mark('onPartData');
|
||||
// falls through
|
||||
case S.PART_DATA:
|
||||
previousIndex = index;
|
||||
|
||||
if (index === 0) {
|
||||
// boyer-moore derrived algorithm to safely skip non-boundary data
|
||||
i += boundaryEnd;
|
||||
while (i < bufferLength && !(data[i] in boundaryChars)) {
|
||||
i += boundaryLength;
|
||||
}
|
||||
|
||||
i -= boundaryEnd;
|
||||
c = data[i];
|
||||
}
|
||||
|
||||
if (index < boundary.length) {
|
||||
if (boundary[index] === c) {
|
||||
if (index === 0) {
|
||||
dataCallback('onPartData', true);
|
||||
}
|
||||
|
||||
index++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index === boundary.length) {
|
||||
index++;
|
||||
if (c === CR) {
|
||||
// CR = part boundary
|
||||
flags |= F.PART_BOUNDARY;
|
||||
} else if (c === HYPHEN) {
|
||||
// HYPHEN = end boundary
|
||||
flags |= F.LAST_BOUNDARY;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index - 1 === boundary.length) {
|
||||
if (flags & F.PART_BOUNDARY) {
|
||||
index = 0;
|
||||
if (c === LF) {
|
||||
// unset the PART_BOUNDARY flag
|
||||
flags &= ~F.PART_BOUNDARY;
|
||||
callback('onPartEnd');
|
||||
callback('onPartBegin');
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
}
|
||||
} else if (flags & F.LAST_BOUNDARY) {
|
||||
if (c === HYPHEN) {
|
||||
callback('onPartEnd');
|
||||
state = S.END;
|
||||
flags = 0;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// when matching a possible boundary, keep a lookbehind reference
|
||||
// in case it turns out to be a false lead
|
||||
lookbehind[index - 1] = c;
|
||||
} else if (previousIndex > 0) {
|
||||
// if our boundary turned out to be rubbish, the captured lookbehind
|
||||
// belongs to partData
|
||||
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
|
||||
callback('onPartData', 0, previousIndex, _lookbehind);
|
||||
previousIndex = 0;
|
||||
mark('onPartData');
|
||||
|
||||
// reconsider the current character even so it interrupted the sequence
|
||||
// it could be the beginning of a new sequence
|
||||
i--;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.END:
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unexpected state entered: ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
dataCallback('onHeaderField');
|
||||
dataCallback('onHeaderValue');
|
||||
dataCallback('onPartData');
|
||||
|
||||
// Update properties for the next call
|
||||
this.index = index;
|
||||
this.state = state;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
end() {
|
||||
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
|
||||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
|
||||
this.onPartEnd();
|
||||
} else if (this.state !== S.END) {
|
||||
throw new Error('MultipartParser.end(): stream ended unexpectedly');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _fileName(headerValue) {
|
||||
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
||||
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = m[2] || m[3] || '';
|
||||
let filename = match.slice(match.lastIndexOf('\\') + 1);
|
||||
filename = filename.replace(/%22/g, '"');
|
||||
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
|
||||
return String.fromCharCode(code);
|
||||
});
|
||||
return filename;
|
||||
}
|
||||
|
||||
export async function toFormData(Body, ct) {
|
||||
if (!/multipart/i.test(ct)) {
|
||||
throw new TypeError('Failed to fetch');
|
||||
}
|
||||
|
||||
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
|
||||
|
||||
if (!m) {
|
||||
throw new TypeError('no or bad content-type header, no multipart boundary');
|
||||
}
|
||||
|
||||
const parser = new MultipartParser(m[1] || m[2]);
|
||||
|
||||
let headerField;
|
||||
let headerValue;
|
||||
let entryValue;
|
||||
let entryName;
|
||||
let contentType;
|
||||
let filename;
|
||||
const entryChunks = [];
|
||||
const formData = new FormData();
|
||||
|
||||
const onPartData = ui8a => {
|
||||
entryValue += decoder.decode(ui8a, {stream: true});
|
||||
};
|
||||
|
||||
const appendToFile = ui8a => {
|
||||
entryChunks.push(ui8a);
|
||||
};
|
||||
|
||||
const appendFileToFormData = () => {
|
||||
const file = new File(entryChunks, filename, {type: contentType});
|
||||
formData.append(entryName, file);
|
||||
};
|
||||
|
||||
const appendEntryToFormData = () => {
|
||||
formData.append(entryName, entryValue);
|
||||
};
|
||||
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
decoder.decode();
|
||||
|
||||
parser.onPartBegin = function () {
|
||||
parser.onPartData = onPartData;
|
||||
parser.onPartEnd = appendEntryToFormData;
|
||||
|
||||
headerField = '';
|
||||
headerValue = '';
|
||||
entryValue = '';
|
||||
entryName = '';
|
||||
contentType = '';
|
||||
filename = null;
|
||||
entryChunks.length = 0;
|
||||
};
|
||||
|
||||
parser.onHeaderField = function (ui8a) {
|
||||
headerField += decoder.decode(ui8a, {stream: true});
|
||||
};
|
||||
|
||||
parser.onHeaderValue = function (ui8a) {
|
||||
headerValue += decoder.decode(ui8a, {stream: true});
|
||||
};
|
||||
|
||||
parser.onHeaderEnd = function () {
|
||||
headerValue += decoder.decode();
|
||||
headerField = headerField.toLowerCase();
|
||||
|
||||
if (headerField === 'content-disposition') {
|
||||
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
||||
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
|
||||
|
||||
if (m) {
|
||||
entryName = m[2] || m[3] || '';
|
||||
}
|
||||
|
||||
filename = _fileName(headerValue);
|
||||
|
||||
if (filename) {
|
||||
parser.onPartData = appendToFile;
|
||||
parser.onPartEnd = appendFileToFormData;
|
||||
}
|
||||
} else if (headerField === 'content-type') {
|
||||
contentType = headerValue;
|
||||
}
|
||||
|
||||
headerValue = '';
|
||||
headerField = '';
|
||||
};
|
||||
|
||||
for await (const chunk of Body) {
|
||||
parser.write(chunk);
|
||||
}
|
||||
|
||||
parser.end();
|
||||
|
||||
return formData;
|
||||
}
|
340
node_modules/node-fetch/src/utils/referrer.js
generated
vendored
Normal file
340
node_modules/node-fetch/src/utils/referrer.js
generated
vendored
Normal file
|
@ -0,0 +1,340 @@
|
|||
import {isIP} from 'node: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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue