Skip to content

Commit 256ca79

Browse files
committed
fix(seed): refuse to run against a prod / preprod database
Address review feedback on #3306: the seed script was relying solely on `EGAPRO_AUTO_SEED_CONFORMITE` (scoped to .kontinuous/env/dev) to stay out of higher environments. Add a defence-in-depth guard inside the script itself so even a mis-configured flag cannot overwrite real declarations with synthetic ones. `assertNotProduction()` aborts with exit 1 when `NEXT_PUBLIC_EGAPRO_ENV` or `EGAPRO_ENV` equals "prod", "production", "preprod" or "preproduction". `EGAPRO_ALLOW_SEED_IN_PROD=true` bypasses the check — kept as a deliberate break-glass escape hatch.
1 parent bb97948 commit 256ca79

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

packages/app/scripts/seed-conformite-stats.mjs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,39 @@ const NAF_SAMPLE_CODES = [
4040
/** Number of most-recent campaign years to seed (current year + N-1 … N-3). */
4141
const CAMPAIGN_YEARS_BACK = 4;
4242

43+
/**
44+
* Hard-fail when the script is about to talk to a production database.
45+
*
46+
* Defence in depth on top of the `EGAPRO_AUTO_SEED_CONFORMITE` opt-in
47+
* flag and the `.kontinuous/env/dev` scoping: even a configuration
48+
* mistake that leaks the flag into preprod or prod cannot end up
49+
* overwriting real declarations with synthetic ones.
50+
*
51+
* Signals that prevent execution, in priority order:
52+
* - `NEXT_PUBLIC_EGAPRO_ENV` equals `"prod"` / `"production"` / `"preprod"`
53+
* (set by kontinuous `app.vars`, matches the values in
54+
* `.kontinuous/env/{prod,preprod}/`).
55+
* - `EGAPRO_ENV` same values (some scripts read this non-public form).
56+
*
57+
* Explicit bypass for CI / emergency: `EGAPRO_ALLOW_SEED_IN_PROD=true`.
58+
* Leaves the seed usable from a break-glass terminal if someone ever
59+
* needs it — but that is a deliberate, logged decision.
60+
*/
61+
function assertNotProduction() {
62+
if (process.env.EGAPRO_ALLOW_SEED_IN_PROD === "true") return;
63+
const signals = [process.env.NEXT_PUBLIC_EGAPRO_ENV, process.env.EGAPRO_ENV]
64+
.filter((v) => typeof v === "string" && v.length > 0)
65+
.map((v) => v.toLowerCase());
66+
const forbidden = ["prod", "production", "preprod", "preproduction"];
67+
const hit = signals.find((v) => forbidden.includes(v));
68+
if (hit) {
69+
console.error(
70+
`[seed-conformite] refusing to run against a ${hit} environment. Set EGAPRO_ALLOW_SEED_IN_PROD=true to override.`,
71+
);
72+
process.exit(1);
73+
}
74+
}
75+
4376
function getDatabaseUrl() {
4477
if (process.env.DATABASE_URL) return process.env.DATABASE_URL;
4578
const user = encodeURIComponent(process.env.POSTGRES_USER ?? "postgres");
@@ -205,6 +238,7 @@ async function clean(sql) {
205238
}
206239

207240
async function main() {
241+
assertNotProduction();
208242
const mode = process.argv[2] ?? "seed";
209243
const sql = postgres(getDatabaseUrl(), { max: 1 });
210244
try {

0 commit comments

Comments
 (0)