Files
ThothII/backend/src/auth/url-policy.ts
T

43 lines
1.4 KiB
TypeScript

export interface ConfiguredTransportUrlOptions {
allowLoopbackHttp: boolean;
originOnly?: boolean;
}
function canonicalLoopbackAuthority(value: string): boolean {
const match = /^http:\/\/([^/?#]+)(?:[/?#]|$)/.exec(value);
if (!match) return false;
const authority = match[1];
let port: string | undefined;
if (authority.startsWith("[")) {
const ipv6 = /^(\[::1\])(?::([^:]+))?$/.exec(authority);
if (!ipv6) return false;
port = ipv6[2];
} else {
const ipv4 = /^([^:]+)(?::([^:]+))?$/.exec(authority);
if (!ipv4) return false;
const octets = ipv4[1].split(".");
if (octets.length !== 4 || octets.some((octet) => !/^(?:0|[1-9]\d{0,2})$/.test(octet)
|| Number(octet) > 255) || Number(octets[0]) !== 127) return false;
port = ipv4[2];
}
return port === undefined || (/^(?:0|[1-9]\d{0,4})$/.test(port) && Number(port) <= 65_535);
}
export function parseConfiguredTransportUrl(
value: string,
options: ConfiguredTransportUrlOptions,
): URL | undefined {
let url: URL;
try {
url = new URL(value);
} catch {
return undefined;
}
if (url.username || url.password || url.search || url.hash || (options.originOnly && url.pathname !== "/")) {
return undefined;
}
if (url.protocol === "https:") return url;
if (options.allowLoopbackHttp && url.protocol === "http:" && canonicalLoopbackAuthority(value)) return url;
return undefined;
}