Files
bare-operating-system/packages/bare-os-booter/lib/services/bare-os-irc-policy.js
T
2026-08-18 18:11:28 -04:00

89 lines
2.5 KiB
JavaScript

/**
* IRC dial policy for ctx.bareOsTlsConnect / /bin/irc.
* BARE_OS_IRC=0 disables. BARE_OS_IRC_DENYLIST always wins.
* Unset allowlist defaults to irc.libera.chat and irc.*.libera.chat.
*/
import { parseHostPatternList } from './bare-os-http-policy.js'
export const BARE_OS_IRC_DEFAULT_ALLOW = [
'irc.libera.chat',
'irc.*.libera.chat'
]
/**
* @param {string} pattern
* @param {string} host
*/
export function bareOsIrcHostMatchesGlob(pattern, host) {
const p = String(pattern || '').toLowerCase()
const h = String(host || '').toLowerCase()
if (p === '*' || p === '*:*') return true
const esc = p
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
.replace(/\*/g, '.*')
.replace(/\?/g, '.')
try {
return new RegExp('^' + esc + '$').test(h)
} catch {
return h === p
}
}
/**
* @param {string} host
*/
export function bareOsIrcIsLiberaHost(host) {
const h = String(host || '')
.trim()
.toLowerCase()
return h === 'libera.chat' || h.endsWith('.libera.chat')
}
/**
* @param {string} host
* @param {Record<string, string | undefined> | null | undefined} env
* @returns {{ ok: boolean, reason?: string }}
*/
export function bareOsIrcHostAllowed(host, env) {
const e = env && typeof env === 'object' ? env : {}
const v = e.BARE_OS_IRC
if (v === '0' || v === 'false') return { ok: false, reason: 'disabled' }
const h = String(host || '')
.trim()
.toLowerCase()
if (!h) return { ok: false, reason: 'missing host' }
const deny = parseHostPatternList(e.BARE_OS_IRC_DENYLIST)
for (const pat of deny) {
if (bareOsIrcHostMatchesGlob(pat, h))
return { ok: false, reason: 'denylist' }
}
const rawAllow = e.BARE_OS_IRC_ALLOWLIST
const allow =
rawAllow == null || String(rawAllow).trim() === ''
? BARE_OS_IRC_DEFAULT_ALLOW
: parseHostPatternList(rawAllow)
if (!allow.length) return { ok: true }
for (const pat of allow) {
if (bareOsIrcHostMatchesGlob(pat, h)) return { ok: true }
}
return { ok: false, reason: 'not in allowlist' }
}
/**
* Libera must not be dialed in plaintext unless the operator forces it twice.
* @param {string} host
* @param {{ tls?: boolean, insecurePlain?: boolean }} opts
*/
export function bareOsIrcPlaintextAllowed(host, opts) {
const o = opts || {}
if (o.tls !== false) return { ok: true }
if (bareOsIrcIsLiberaHost(host) && o.insecurePlain !== true) {
return {
ok: false,
reason: 'libera requires TLS (use --insecure-plain to override)'
}
}
return { ok: true }
}