66 lines
1.7 KiB
JavaScript
66 lines
1.7 KiB
JavaScript
/**
|
|
* Optional strict DNS-style host allowlist for HTTP clients (`BARE_OS_DNS_ALLOWLIST`).
|
|
* Comma/whitespace-separated hostnames; `*.example.com` suffix wildcard when entry starts with `*.`.
|
|
*/
|
|
|
|
/**
|
|
* @param {string} host
|
|
* @param {string[]} allow
|
|
*/
|
|
function hostMatchesAllowlist(host, allow) {
|
|
const h = String(host)
|
|
.toLowerCase()
|
|
.replace(/\.$/, '')
|
|
for (const raw of allow) {
|
|
const a = String(raw)
|
|
.toLowerCase()
|
|
.replace(/\.$/, '')
|
|
if (!a) continue
|
|
if (a.startsWith('*.')) {
|
|
const root = a.slice(2)
|
|
if (!root) continue
|
|
if (h === root) return true
|
|
if (h.endsWith('.' + root)) return true
|
|
continue
|
|
}
|
|
if (h === a) return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* @param {Record<string, string | undefined>} env
|
|
* @returns {string[] | null} null = policy disabled
|
|
*/
|
|
export function parseDnsAllowlistFromEnv(env) {
|
|
const raw = env && env.BARE_OS_DNS_ALLOWLIST
|
|
if (raw == null || raw === '' || raw === '0' || raw === 'false') return null
|
|
const list = String(raw)
|
|
.split(/[\s,]+/)
|
|
.map((s) => s.trim())
|
|
.filter(Boolean)
|
|
return list.length ? list : null
|
|
}
|
|
|
|
/**
|
|
* @param {string} urlString
|
|
* @param {Record<string, string | undefined>} env
|
|
*/
|
|
export function assertFetchUrlHostAllowedByDnsPolicy(urlString, env) {
|
|
const allow = parseDnsAllowlistFromEnv(env)
|
|
if (!allow) return
|
|
let u
|
|
try {
|
|
u = new URL(urlString)
|
|
} catch {
|
|
return
|
|
}
|
|
if (u.protocol !== 'http:' && u.protocol !== 'https:') return
|
|
const host = u.hostname
|
|
if (!host) return
|
|
if (hostMatchesAllowlist(host, allow)) return
|
|
throw new Error(
|
|
`BARE_OS_DNS_ALLOWLIST: host not allowed: ${host} (url ${urlString.slice(0, 120)})`
|
|
)
|
|
}
|