safe-shell — refuse destructive Bash commands
Fixes: UpGuard / ClaudeLog YOLO-mode postmortems (Dec 2025) — documented home-directory deletions and rm -rf / from root when Claude Code ran with --dangerously-skip-permissions and no supervision.
What this prevents
"A Claude Code session running with --dangerously-skip-permissions
issued rm -rf ~/ while attempting to 'clean up the project
directory.' The user lost their entire home folder." — Dec 2025
postmortem
Anthropic's own permission prompt is the safety net for destructive
commands. In YOLO mode (--dangerously-skip-permissions or
--permission-mode bypassPermissions), that net is gone. safe-shell
sits below the permission layer and refuses a curated list of
irreversible operations regardless of permission mode.
What gets blocked
| Category |
Examples |
| Filesystem wipes |
rm -rf /, rm -rf ~/, rm -rf $HOME, rm --no-preserve-root |
| Credential / git destruction |
rm -rf .git, rm -rf ~/.ssh, git reset --hard HEAD~3, git clean -fd, git branch -D |
| Force-push |
git push --force, git push -f (including --force-with-lease) |
| Disk-level |
mkfs, fdisk, parted, dd of=/dev/sd* |
| Permission nukes |
chmod -R 777 /, chown -R … / |
| Remote-code-exec |
curl … | sh, wget … | bash |
| Fork bombs |
:(){ :|:& };: |
Commands that look destructive but operate on local project paths
(rm -rf node_modules, rm -rf ./dist, git reset --hard HEAD) are
allowed — the list is curated for catastrophic and irreversible
operations only.
How it works (zero manual work for the user)
Claude wants to call Bash with command X
│
▼
PreToolUse hook fires
│
▼
Match X against the block-list regex set
│
┌────┴────┐
│ │
▼ ▼
No match Match
│ │
▼ ▼
exit 0 Emit { hookSpecificOutput.permissionDecision: "deny",
silent permissionDecisionReason: "<explanation>" }
on stdout. Claude Code refuses the call and feeds the
reason back to Claude as an error.
When you (the model) should invoke this skill manually
- User runs
/claude-papercuts:safe-shell
- User asks "what does safe-shell block?"
- User asks why a Bash command was refused
- User asks how to safely run a destructive operation themselves
Manual invocation procedure
- Read
${CLAUDE_PLUGIN_ROOT}/skills/safe-shell/hooks/guard.sh to
see the current rule set.
- Show the user the categories and examples in the table above.
- If the user mentions a specific command, tell them whether it
matches a block-list pattern (read the regex set, don't guess).
- Never edit the rule set on your own. If the user wants to add or
remove a rule, ask them to file an issue at
https://github.com/dhruba-datta/claude-papercuts/issues.
- If a command is blocked and the user genuinely wants to run it,
tell them to run it in their own shell — safe-shell intentionally
has no override flag.
What this skill does NOT do
- It is not a complete security layer. It refuses the highest-
hazard, most-irreversible commands. It does not stop crafted
obfuscation (e.g.
r''m -rf /, base64-encoded payloads, multi-step
scripts). Treat it as a seatbelt, not a vault.
- It does not log blocked attempts. The block is visible to Claude
via the hook reason; nothing is written to disk.
- It does not warn before blocking. No "are you sure" prompt — the
command is refused outright. The user is expected to run intentional
destructive operations in their own shell.
- No override flag. Even with
--dangerously-skip-permissions, the
block stands.
Privacy
No network calls. Hook reads only the Bash command string passed by
Claude Code.
Deprecation plan
If Anthropic ships a first-class destructive-command refusal layer
that survives --dangerously-skip-permissions, this skill becomes a
duplicate and gets deprecated in the next monthly release with the
date.
1---2name: safe-shell3description: Block destructive Bash commands before Claude executes them — even in --dangerously-skip-permissions (YOLO) mode where Anthropic's own permission prompts are bypassed. Use this skill when the user runs /claude-papercuts:safe-shell, asks what safe-shell blocks, or wants to know why a command they expected was refused. A PreToolUse hook scans every Bash tool call against a list of irreversible patterns (rm -rf against / or ~, git push --force, git reset --hard HEAD~, mkfs, dd to /dev/sda, curl-pipe-bash, fork bombs) and refuses them with a structured explanation. Block decisions are visible to Claude so it can re-plan.4---56# safe-shell — refuse destructive Bash commands78**Fixes:** [UpGuard / ClaudeLog YOLO-mode postmortems (Dec 2025)](https://www.upguard.com/blog/claude-code-cybersecurity-risks) — documented home-directory deletions and `rm -rf /` from root when Claude Code ran with `--dangerously-skip-permissions` and no supervision.910## What this prevents1112> *"A Claude Code session running with `--dangerously-skip-permissions`13> issued `rm -rf ~/` while attempting to 'clean up the project14> directory.' The user lost their entire home folder."* — Dec 202515> postmortem1617Anthropic's own permission prompt is the safety net for destructive18commands. In YOLO mode (`--dangerously-skip-permissions` or19`--permission-mode bypassPermissions`), that net is gone. `safe-shell`20sits *below* the permission layer and refuses a curated list of21irreversible operations *regardless* of permission mode.2223## What gets blocked2425| Category | Examples |26|---|---|27| Filesystem wipes | `rm -rf /`, `rm -rf ~/`, `rm -rf $HOME`, `rm --no-preserve-root` |28| Credential / git destruction | `rm -rf .git`, `rm -rf ~/.ssh`, `git reset --hard HEAD~3`, `git clean -fd`, `git branch -D` |29| Force-push | `git push --force`, `git push -f` (including `--force-with-lease`) |30| Disk-level | `mkfs`, `fdisk`, `parted`, `dd of=/dev/sd*` |31| Permission nukes | `chmod -R 777 /`, `chown -R … /` |32| Remote-code-exec | `curl … \| sh`, `wget … \| bash` |33| Fork bombs | `:(){ :\|:& };:` |3435Commands that look destructive but operate on local project paths36(`rm -rf node_modules`, `rm -rf ./dist`, `git reset --hard HEAD`) are37allowed — the list is curated for catastrophic and irreversible38operations only.3940## How it works (zero manual work for the user)4142```43Claude wants to call Bash with command X44 │45 ▼46PreToolUse hook fires47 │48 ▼49Match X against the block-list regex set50 │51 ┌────┴────┐52 │ │53 ▼ ▼54No match Match55 │ │56 ▼ ▼57exit 0 Emit { hookSpecificOutput.permissionDecision: "deny",58silent permissionDecisionReason: "<explanation>" }59 on stdout. Claude Code refuses the call and feeds the60 reason back to Claude as an error.61```6263## When you (the model) should invoke this skill manually6465- User runs `/claude-papercuts:safe-shell`66- User asks "what does safe-shell block?"67- User asks why a Bash command was refused68- User asks how to safely run a destructive operation themselves6970## Manual invocation procedure71721. Read `${CLAUDE_PLUGIN_ROOT}/skills/safe-shell/hooks/guard.sh` to73 see the current rule set.742. Show the user the categories and examples in the table above.753. If the user mentions a specific command, tell them whether it76 matches a block-list pattern (read the regex set, don't guess).774. Never edit the rule set on your own. If the user wants to add or78 remove a rule, ask them to file an issue at79 `https://github.com/dhruba-datta/claude-papercuts/issues`.805. If a command is blocked and the user genuinely wants to run it,81 tell them to run it in their own shell — safe-shell intentionally82 has no override flag.8384## What this skill does NOT do8586- **It is not a complete security layer.** It refuses the highest-87 hazard, most-irreversible commands. It does not stop crafted88 obfuscation (e.g. `r''m -rf /`, base64-encoded payloads, multi-step89 scripts). Treat it as a seatbelt, not a vault.90- **It does not log blocked attempts.** The block is visible to Claude91 via the hook reason; nothing is written to disk.92- **It does not warn before blocking.** No "are you sure" prompt — the93 command is refused outright. The user is expected to run intentional94 destructive operations in their own shell.95- **No override flag.** Even with `--dangerously-skip-permissions`, the96 block stands.9798## Privacy99100No network calls. Hook reads only the Bash command string passed by101Claude Code.102103## Deprecation plan104105If Anthropic ships a first-class destructive-command refusal layer106that survives `--dangerously-skip-permissions`, this skill becomes a107duplicate and gets deprecated in the next monthly release with the108date.