/* Host guard for every UI-driving (Playwright) admin script. The REST write path is already structurally safe: `dol-write.sh` refuses any host that is not the sandbox (ADR-0003). The UI path had no equivalent — and `test/.env` ships DOLIBARR_ADDRESS pointing at PRODUCTION, so a script run with the ambient environment would drive the real ERP. This module closes that gap: an admin script calls `assertSandbox()` before its first click, and dies otherwise. Production changes are never made by a script. They are rehearsed here, then applied by the operator through the human-gated path. */ /** Hosts an admin script is allowed to drive. Sandbox only, by design. */ const ALLOWED_HOST_PATTERN = /^erp-sandbox\./i; export class UnsafeTargetError extends Error {} /** * Resolve the target address and refuse anything that is not the sandbox. * Pass an explicit address, or let it read DOLIBARR_ADDRESS from the env. */ export function assertSandbox(address?: string): string { const target = address ?? Deno.env.get("DOLIBARR_ADDRESS") ?? ""; if (!target) { throw new UnsafeTargetError( "guard: no target address (pass one, or set DOLIBARR_ADDRESS)", ); } let host: string; try { host = new URL(target).host; } catch { throw new UnsafeTargetError(`guard: not a valid URL: ${target}`); } if (!ALLOWED_HOST_PATTERN.test(host)) { throw new UnsafeTargetError( `REFUSED: '${host}' is not the sandbox.\n` + "UI admin scripts may only drive erp-sandbox.*; production is changed " + "by the operator through the human-gated path, never by a script.\n" + "Override the ambient env explicitly, e.g.\n" + " DOLIBARR_ADDRESS=https://erp-sandbox.arcodange.lab deno run ...", ); } return target; } export default { assertSandbox, UnsafeTargetError };