Skip to content

DR-103: destructive scripts fail closed on an explicit target allowlist

DR-103: destructive scripts fail closed on an explicit target allowlist

Section titled “DR-103: destructive scripts fail closed on an explicit target allowlist”

The id-400 pre-run sweep (scripts/cocoindex-nightly-sweep.ts) shipped with a prod-denylist guard: refuse when SUPABASE_URL contains PROD_PROJECT_REF. Review (PR #156, CodeRabbit finding) showed the guard fails open: with the env var unset — one missing variable in a misconfigured CI environment — the only protection on a destructive multi-table delete silently disappears. The same shape had already been recorded in the machine-signal register (id-379 instance 8, id-381): a guard that is present and executing but watching too weak a signal.

A script that deletes or rewrites database rows MUST fail closed on an explicit target allowlist: it requires a positively-supplied expected-target identifier (e.g. KH_SWEEP_EXPECTED_PROJECT_REF) and refuses unless the live connection target matches it. A missing guard input is a refusal, never a pass. Denylists (refuse the prod ref) may be kept in addition, but are never sufficient alone. The invoking workflow must supply the expected identifier explicitly, so the authorisation is visible at the call site.

Reference implementation: scripts/cocoindex-nightly-sweep.ts + .github/workflows/cocoindex-nightly.yml (id-400, commit edbc9e25).

  • Denylist only (refuse prod) — rejected: fails open on missing env, and protects only the one environment it names; every other wrong target passes.
  • Require the env var but keep the denylist shape — rejected as the general rule: still answers “is this NOT prod” rather than “is this the intended target”.
  • Interactive confirmation — not viable for CI-invoked scripts.
  • New destructive scripts (sweeps, resets, backfills, mass deletes) copy the allowlist shape; review treats a denylist-only or env-presence guard on a destructive path as a defect.
  • Guard inputs appear explicitly in workflow env blocks, making the intended target auditable per invocation.
  • Existing destructive scripts are brought under the rule opportunistically as they are next touched (no dedicated sweep mandated by this decision).