Auto mode + ask rules failed to gate destructive command: `prisma migrate diff --shadow-database-url` wiped a production database
Summary
During an agent session (Claude Code 2.1.218, VSCode extension, model claude-fable-5, defaultMode: "auto"), the model generated a Prisma migration diff and passed the project's real DATABASE_URL as --shadow-database-url:
cd "<repo>" && MIG="prisma/migrations/$(date +%Y%m%d%H%M%S)_<name>" && mkdir -p "$MIG" && \
npx prisma migrate diff --from-migrations prisma/migrations \
--to-schema-datamodel prisma/schema.prisma \
--shadow-database-url "$(grep -E '^DATABASE_URL=' <path>/.env | cut -d= -f2- | tr -d '\"')" \
--script > "$MIG/migration.sql"
Prisma resets the shadow database (drops all objects) before replaying migrations. The URL pointed at a production Postgres — the database was wiped (all rows lost, restored later from a nightly backup; one paying customer's account was in the loss window).
The model error itself (choosing a real URL as shadow DB) is one layer. This issue is about the harness layer that should have caught it and didn't.
Expected gating that did not fire
The user's global ~/.claude/settings.json had these ask rules configured before the incident:
"ask": [
"Bash(npx prisma db:*)",
"Bash(npx prisma migrate:*)",
...
]
Expected: the command should have prompted for manual approval.
Actual: no prompt — the command executed immediately.
Likely cause: the dangerous invocation was embedded in a compound command (cd … && VAR=… && mkdir … && npx prisma migrate diff … > file) containing a command substitution ($(grep …)). It appears the permission-rule matcher did not match the nested npx prisma migrate … subcommand, and the auto-mode classifier then allowed the compound (plausibly classified as benign SQL-file generation — the destructive semantics of --shadow-database-url are non-obvious).
Notably, the same session's classifier correctly hard-blocked other risky calls (a Stripe price-creation POST, an SSH attempt), so the classifier was active — it just did not recognize this one.
Suggestions
- Match allow/ask/deny Bash rules against decomposed subcommands of compound commands, including commands that contain substitutions — an ask rule for
npx prisma migrate:*should fire when that string is any command position of a chain. - Teach the auto-mode classifier that
prisma migrate diff/migrate devwith--shadow-database-url <non-throwaway URL>is destructive (Prisma resets the shadow DB), same category asmigrate reset/db push --accept-data-loss. - Consider a built-in soft-block for
--shadow-database-urlvalues that come from env/.envfiles rather than obviously ephemeral local DBs.
Environment
- Claude Code 2.1.218 (VSCode native extension)
- Model: claude-fable-5
- macOS (Darwin 25.5.0)
- permissions.defaultMode: "auto"
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗