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)	// BUG: CWE-400 Uncontrolled Resource Consumption	// if (/^(.+\.)*localhost$/.test(url.host)) {	// FIXED: 	if (url.host === 'localhost' || url.host.endsWith('.localhost')) {		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;}