Files
dsh/upstream/container/control-plane/services/gateway/lib/trust.mjs
T

57 lines
2.0 KiB
JavaScript

function parseRequestAuthority(value) {
if (
value === ''
|| /[\s/@?#]/.test(value)
|| value.endsWith(':')
) return undefined
try {
const parsed = new URL(`http://${value}`)
return parsed.hostname === '' ? undefined : parsed
} catch {
return undefined
}
}
export function isLoopbackHostname(hostname) {
if (hostname === 'localhost' || hostname === '[::1]') return true
const parts = hostname.split('.')
return parts.length === 4
&& parts[0] === '127'
&& parts.every(part => /^\d{1,3}$/.test(part) && Number(part) <= 255)
}
function matchesTrustedAuthority(requestUrl, trustedHosts) {
if (trustedHosts.wildcard) return true
return trustedHosts.authorities.some((entry) => (
entry.anyPort ? entry.hostname === requestUrl.hostname : entry.matchAuthority === requestUrl.host
))
}
export function inspectExternalRequest(headers, trustedHosts, {
allowCrossOrigin = false,
allowUntrustedAuthority = false,
} = {}) {
const host = headers.host
if (typeof host !== 'string') return Object.freeze({ accepted: false, reason: 'missing-host' })
const hostUrl = parseRequestAuthority(host)
if (hostUrl === undefined) return Object.freeze({ accepted: false, reason: 'invalid-host' })
if (!allowUntrustedAuthority && !isLoopbackHostname(hostUrl.hostname) && !matchesTrustedAuthority(hostUrl, trustedHosts)) {
return Object.freeze({ accepted: false, reason: 'untrusted-host' })
}
if (!allowCrossOrigin && headers['sec-fetch-site'] === 'cross-site') {
return Object.freeze({ accepted: false, reason: 'cross-site' })
}
const origin = headers.origin
if (!allowCrossOrigin && origin !== undefined) {
if (typeof origin !== 'string') return Object.freeze({ accepted: false, reason: 'invalid-origin' })
try {
if (new URL(origin).host !== hostUrl.host) {
return Object.freeze({ accepted: false, reason: 'origin-mismatch' })
}
} catch {
return Object.freeze({ accepted: false, reason: 'invalid-origin' })
}
}
return Object.freeze({ accepted: true, externalAuthority: hostUrl.host })
}