feat(plugin): add destructive-command-guard plugin#23946
feat(plugin): add destructive-command-guard plugin#23946leszekszpunar wants to merge 9 commits intoanthropics:mainfrom
Conversation
…xamples Options in interactive-commands.md Pattern 2 used inline comma-separated format without descriptions, causing skill-creator to crash with 'description.split is not a function' when generating AskUserQuestion tool calls from these patterns. Convert all bare option lists to bulleted format with parenthetical descriptions, consistent with the AskUserQuestion tool schema and the rest of the reference document. Fixes anthropics#23855
Add a PreToolUse hook plugin that protects against self-destructive agent operations (addresses anthropics#23871 and anthropics#23870): - Block dangerous mass-deletion commands targeting /, ~, ., .., *, $HOME - Block mass Docker operations (system prune, volume prune, container mass-removal via subshell, compose down with volumes) - Block destructive git commands (clean without dry-run, checkout all changes, hard reset without explicit target) - Warn (once per session) on edits to policy files (CLAUDE.md, .claude/settings.json, .claude/settings.local.json, hooks.json) - Support multi-command chains (&&, ;, |) splitting - Disable via ENABLE_DESTRUCTIVE_GUARD=0 env var - Session-scoped state with automatic cleanup (30 days)
Address bypass vectors found during adversarial security review and Gemini code analysis: - Add indirect execution blocking (eval, sh -c, bash -c, pipe-to-shell, base64 decode to shell) - Add system path protection for rm (covers /etc, /usr, /var, /home, /boot, /opt, /bin, /sbin, /lib, /Users, /Applications, /System) - Add path normalization to catch //, /./, /../.. variants - Add command substitution and variable expansion detection in rm targets - Add backtick detection in rm arguments - Add Docker container/image/network/builder prune blocking - Add git push --force, git branch -D, git stash clear blocking - Add file protection via Bash commands (echo >, sed -i, mv, cp, tee, truncate, dd targeting CLAUDE.md and policy files) - Add find -delete blocking on dangerous paths - Fix symlink attack vector: move debug log from /tmp to ~/.claude - Fix path traversal: sanitize session_id input (CWE-22) - Fix race condition: use atomic writes via tempfile + os.replace - Document security model limitations in README
Replace all 68 Polish comments and docstrings in destructive_command_guard.py with English equivalents to maintain consistency with the rest of the repository.
…ss ops Address additional destructive patterns found in related open issues: - Block 'git stash drop' without a specific stash ref (anthropics#22638: user lost 3 days of stashed work). 'git stash drop stash@{N}' still allowed. - Block 'docker volume rm $(docker volume ls -q)' mass removal (anthropics#14944: Supabase data volumes deleted). Named volume removal still allowed. - Update README with new patterns
Address four bypass vectors identified during final Gemini code analysis: - Fix command splitter to handle || operator and newline separators (previously only split on &&, ;, |) - Block find -exec rm and find -execdir rm on dangerous paths - Block xargs rm piped from find on dangerous paths - Update README with expanded alternative deletion patterns
…lookup Replace fragile substring-based message matching in check_bash_file_modification with a call to check_file_protection(), ensuring consistent message resolution across Write/Edit and Bash code paths.
|
Update: Added |
|
Hey @leszekszpunar — nice work on the safety coverage here, clearly a lot of thought went into it. Just wanted to flag that Hardstop (npm i hardstop) already covers this space as a published Claude Code plugin (v1.4.3), and was submitted through the official plugin submission process. It's been on npm for a while now with:
There's significant overlap between the two projects. I'd encourage the maintainers to consider the existing ecosystem work here before merging a parallel implementation. Happy to collaborate if there are gaps worth addressing together. |
|
Hey @frmoretto — thanks for the heads-up, and nice work on Hardstop. I've reviewed the codebase and patterns in detail. You're right there's overlap, but I think the two plugins occupy different design points rather than being direct duplicates. Where Hardstop clearly wins:
Where this plugin goes deeper:
Different design philosophy:
I think these are genuinely complementary. If maintainers are interested, one path forward could be combining the deep semantic analysis from this plugin (git, Docker, policy files, Write/Edit monitoring) with the broader pattern library from |
…ted text Add _strip_quoted_content() that neutralizes content inside single quotes, double quotes (with backslash escape handling), and heredocs before pattern matching. This prevents false positives when dangerous patterns like rm -rf or git reset --hard appear as text arguments rather than executable commands (e.g., gh pr comment --body "text about rm -rf"). handle_bash() now strips quoted content once and passes the safe version to all check functions. Actual dangerous commands remain blocked -- eval, bash -c, and other indirect execution vectors are still caught because the command structure (first token) survives stripping.
|
Update (March 20): Since this PR was submitted, destructive-command incidents have continued to escalate. Recent reports from the last few days:
There's also growing community momentum around safety plugins — several new guard PRs since February: #34257, #33390, #31633, #30521, #30692. This plugin remains conflict-free and opt-in. It specifically addresses git/Docker/filesystem destruction patterns — areas where broader pattern-matching approaches have documented gaps (e.g., Would appreciate a review when the team has bandwidth. Happy to adapt to any feedback. |
Summary
destructive-command-guardplugin -- a PreToolUse hook that blocks irreversible Bash commands and warns about edits to agent policy filessystemMessage) on edits toCLAUDE.md,.claude/settings.json,.claude/settings.local.json,hooks/hooks.jsonENABLE_DESTRUCTIVE_GUARD=0), and session-scoped state with 30-day auto-cleanupCloses #23871, closes #23870
Related issues
This plugin addresses or mitigates multiple open reports of destructive agent behavior:
git stash dropcausing data lossgit stash dropwithout specific refgit checkoutwithout confirmationgit checkout -- .patterndocker volume rmwithout confirmation, Supabase data lossdocker volume rm $(docker volume ls -q)git reset --hardduring test restructuringgit reset --hardwithout explicit targetrm -rfin chained command caused data lossrm -rfto delete project directory\nseparators, checks each part independentlyNot covered (out of scope for this plugin):
git checkout <file>(Bug: Claude executes destructive git checkout without confirmation #18290 partial) -- cannot distinguish from branch switching without filesystem contextSecurity hardening
~/.claude/not/tmp(CWE-377 symlink attack prevention)tempfile+os.replace(race condition mitigation)//,/./,/../..)||, newlines,&&,;,|Test plan
claude --plugin-dir ./plugins/destructive-command-guardand test end-to-end