feat(test): host guard for UI admin scripts + sandbox legal-mentions setup
test/.env ships DOLIBARR_ADDRESS pointing at PRODUCTION and test/main.ts defaults to it, so any Playwright admin script run with the ambient environment drives the real ERP. The REST path has been structurally safe since ADR-0003 (dol-write.sh refuses non-sandbox hosts); the UI path had no equivalent. scripts/guard.ts closes that gap — assertSandbox() resolves the target and refuses anything that is not erp-sandbox.*, with the override spelled out in the error. Verified: an unqualified run now dies instead of reaching prod. Two settings the write agent cannot reach (non-admin by design, 403 on /setup/conf), rehearsed on the sandbox: - sandboxLegalSetup.ts — capital social + multi-currency module. Finding: the capital was NOT missing, it was stored as "1000€"; the symbol made the value unusable by the PDF template, which is why "Capital de 1 000 €" was absent from every invoice since January (C. com. R.123-238). Normalised to "1000" → the mention now renders. - sandboxCurrencySetup.ts — registers the USD reference rate. Enabling the module is not enough: a currency absent from the rate table makes Dolibarr silently fall back to EUR (observed on a probe invoice). With the rate registered, an invoice carries USD 3,000.00 with its EUR counter-value, i.e. the contractual obligation itself rather than a drifting equivalent. Both scripts are report-only when they cannot recognise a form, and screenshot what they did. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 };
|
||||
Reference in New Issue
Block a user