# How to Block Dangerous Commands From a Coding Agent (2026)

Five ways to stop an AI coding agent from running rm -rf, force-pushes, and disk writes — permission prompts, deny lists, dcg, a local policy floor, and devcontainers — ranked by what they actually catch.


<p><sub>Real recording, nothing mocked: a coding agent tries to force-push <code>main</code>; the safety floor blocks it in the call path <em>before</em> it runs — no policy tuning, no prompt. Then a network call is held for approval, normal work is allowed, and the closing <code>audit.jsonl</code> shows every decision. This is option #4 below, running.</sub></p>

The failure mode is documented at this point: an agent gets a confident idea,
runs a destructive command, and the transcript reads like an apology. A
[database deleted during a code freeze](/blog/recreated-replit-database-deletion),
a [home directory wiped by one malformed command](/blog/recreated-amazon-q-filesystem-wipe).
The model cannot be the safety layer — the layer that says no has to sit
outside it, in the call path.

Five ways to put it there, ranked by what they actually catch. The criteria:
is the block **deterministic** (not a suggestion the model can talk itself
past), does it survive **respelling** (`rm -r -f`, `bash -c` wrappers,
chains after `&&`), does it cover **every agent you run**, and does it leave
**a record**.

## 1. The permission prompts you already have

Claude Code, Codex, and Cursor all ship approval prompts, and if you run
with them on, most catastrophes die in the dialog. This is the baseline and
it's genuinely good. Two ways it erodes: approval fatigue (the hundredth
"yes" is a reflex, not a review), and `--dangerously-skip-permissions` — the
moment the workflow gets annoying enough, the prompts go away entirely, and
[the flag is popular](/blog/claude-code-dangerously-skip-permissions).

## 2. Built-in deny lists

One step better: rules like Claude Code's `permissions.deny`. Free, native,
and worth setting — [our recommended list](/blog/which-claude-code-tools-to-deny-out-of-the-box).
The catch: they match strings. `rm -rf /` is denied; `rm -r -f /` or
`bash -c "rm -rf /"` may not be, and [we've written up how the deny list
gets bypassed](/blog/claude-code-deny-list-bypass). String rules are a
tripwire, not a wall. They're also per-tool config — your Codex rules are a
different file in a different dialect.

## 3. dcg — the dedicated shell tripwire

[dcg](https://github.com/Dicklesworthstone/destructive_command_guard) does
one thing well: a Rust hook that blocks destructive shell commands for
Claude Code, Codex, and Gemini CLI, with far more spelling-awareness than a
string deny list. If all you want is "stop `rm -rf`", it's the best
single-purpose tool in the category and the right minimal answer. What it
doesn't try to be: a policy layer (allow/ask/deny per tool, per subcommand,
per host), or an audit trail of everything else the agent did.

## 4. A local policy floor in the hook path

This is the layer we build, so judge accordingly — but the mechanism is the
point, and it's open. Every one of these harnesses fires a pre-tool-call
hook; whatever answers that hook can deny the call deterministically, and
hooks keep firing even under `--dangerously-skip-permissions`. So: parse
every segment of the command token-by-token (catching the respellings and
the `bash -c` laundering), deny a short catastrophic floor unconditionally,
apply your own `allow`/`ask`/`deny` rules for everything else, and log every
decision. One command installs it for Claude Code, Codex, and Cursor at
once, no account, on-device:

```bash
curl -sf https://agenticcontrolplane.com/install.sh | bash -s -- --local
```

The decision engine is [a few hundred lines you can read](https://github.com/agentic-control-plane/acp-install/blob/main/decide.mjs);
the [60-second recording](/blog/control-your-coding-agent-in-one-command)
shows a real force-push dying at the hook. Weakness, stated plainly: it's
not isolation, and a hook can only govern the harness it's wired into — a
raw `curl | sh` you run yourself is out of scope.

## 5. Devcontainers — when the input is untrusted

If your agent ingests untrusted content — public issues, scraped pages,
other people's PRs — the right answer is a boundary, not a filter:
devcontainers or a VM. Nothing command-level protects you from what a
[prompt-injected agent does with the access it legitimately has](/blog/recreated-supabase-cursor-lethal-trifecta).
Containment doesn't replace command control, though: inside the container,
your repo credentials still work, and a force-push to `main` is still a
force-push. Run both.

## The honest summary

Prompts on → you're mostly fine until fatigue wins. Deny lists → set them,
know they're bypassable. dcg → best pure tripwire. A hook-path policy floor →
deterministic, respelling-proof, cross-vendor, logged — our lane. Untrusted
input → container first, then all of the above inside it.


