引言
"AI Agent 偶尔会运行灾难性的命令------
rm -rf ./src、git reset --hard、DROP TABLE users------瞬间销毁数小时未提交的工作。"
这是"每日一个开源项目"系列的第160篇文章 。今天的主角是 Destructive Command Guard(dcg)------一个在 AI 编程 Agent 执行危险命令之前拦截它的 Rust 钩子工具。
你可能遇到过这种场景:让 Claude Code 清理临时文件,结果它用 rm -rf 删了整个 src 目录;或者让它重置一个 Git 状态,结果 git reset --hard 把你两小时的未提交工作全部删光。
这不是 AI 模型变傻了,而是 AI Agent 对"破坏性"的理解和人类不同------它知道命令会做什么,但可能低估了在你当前具体状态下的代价。
dcg 的解决方案:在命令执行之前加一道门,把已知的危险模式拦下来,把决策权还给用户。
你将学到什么
- dcg 的四阶段处理流水线:如何在亚毫秒内完成拦截判断
- 上下文感知的关键设计:为什么
grep "rm -rf"不该被拦截 - 50+ 安全规则包的覆盖范围
- Heredoc 扫描:如何处理
python -c "os.remove()"这类隐式命令 - 三种绕过方式(bypass)的设计
- Claude Code 的 PreToolUse 钩子集成
前置知识
- 日常使用 Claude Code、Cursor 或类似 AI 编程工具
- 了解基本的 shell 命令和 Git 操作
- 有过 AI Agent 执行危险操作的直接或间接经历更容易理解这个工具的价值
项目背景
项目简介
Destructive Command Guard(dcg)是一个挂载在 AI 编程 Agent 的命令执行钩子上的防护工具,用 Rust 实现,以 PreToolUse 钩子的形式接入 Claude Code 等工具的执行流程。
命令在被执行之前先经过 dcg 的过滤:如果命令匹配到已知的危险模式,dcg 输出拦截信息,命令不执行;如果判断是安全的,命令正常执行,延迟小于 1 毫秒。
作者介绍
- Jeffrey Emanuel:最初的 Python 概念,扩展了 Rust 实现(规则包系统、Heredoc 扫描、上下文分类)
- Darin Gordon:初始 Rust 移植和性能优化
- License: MIT
项目数据
- ⭐ GitHub Stars: 4,400+
- 🍴 Forks: 165+
- 📄 License: MIT
- 🦀 语言: Rust(Edition 2024,nightly)
四阶段处理流水线
每一个命令从 Agent 发出到实际执行,都要经过这四步:
python
AI Agent 想执行一个命令
↓
Stage 1: JSON 解析
接收 PreToolUse 钩子的 JSON 负载
提取命令字符串
无效负载 → fail-open(不拦截,直接放行)
↓
Stage 2: 命令规范化
/usr/bin/git → git
/opt/homebrew/bin/python3 → python3
消除绝对路径带来的误判
↓
Stage 3: 快速过滤
SIMD 加速的子字符串扫描
快速跳过 99%+ 的安全命令
触发可疑关键词 → 进入完整分析
↓
Stage 4: 模式匹配
先检查允许列表(安全模式)
再检查拒绝列表(危险模式)
拒绝 → 输出拦截信息 + 给出替代建议
允许 → 命令正常执行
总延迟:< 1ms(设有 200ms 绝对超时,超时则 fail-open)
Fail-open 设计原则:当 dcg 自身出现错误(解析失败、超时、未预期的 panic)时,它选择放行命令而不是拦截。这保证了 dcg 本身不会因为出现 bug 而卡住你的工作流------安全防护是附加价值,不能变成一个新的故障点。
默认开启的保护
安装后无需配置,以下保护立即生效:
core.filesystem(永久开启,不可关闭)
拦截临时目录之外的危险删除操作:
bash
# 会被拦截:
rm -rf ./src
rm -rf ~/Documents
find . -name "*.js" -delete
# 不会被拦截(合理操作):
rm -rf /tmp/build_cache
rm /tmp/test_output.log
core.git(默认开启)
拦截会破坏提交历史或丢失未提交工作的 Git 操作:
bash
# 会被拦截:
git reset --hard HEAD~5 # 丢失未提交改动
git push --force # 覆盖远端历史
git clean -fd # 删除未追踪文件
git rebase --root # 重写整个历史
# 拦截提示示例:
# ════════════════════════════════════════════════
# BLOCKED dcg
# ────────────────────────────────────────────────
# Reason: git reset --hard destroys uncommitted changes
# Command: git reset --hard HEAD~5
# Tip: Consider using 'git stash' first.
# ════════════════════════════════════════════════
system.disk(默认开启)
拦截磁盘级别的危险操作:
bash
# 会被拦截:
mkfs.ext4 /dev/sda
dd if=/dev/zero of=/dev/sdb
fdisk /dev/sda
上下文感知:最关键的设计
dcg 必须区分"命令字符串出现在数据上下文"和"命令字符串在执行上下文":
bash
# 这些不应该被拦截:
grep "rm -rf" README.md # 搜索字符串,不是执行命令
cat ~/.bashrc | grep "reset" # 查看配置,不是执行
echo "never run: rm -rf /" # 输出字符串
# 这些应该被拦截:
rm -rf ./important_dir
$(rm -rf /) # 命令替换中的危险命令
`git reset --hard` # 反引号执行
context classification 模块通过分析命令的语义角色(是作为参数传递给 grep/echo/cat,还是作为独立可执行命令?)来做这个判断。
Heredoc 和内联代码扫描
这是很多同类工具做不到的能力:扫描传递给解释器的内联代码:
bash
# 这类攻击会被扫描到:
python -c "import os; os.remove('/etc/passwd')"
python -c "import shutil; shutil.rmtree('./src')"
bash -c "rm -rf /var/log/*"
node -e "require('fs').rmdirSync('./dist', {recursive: true})"
# Heredoc 形式:
python3 << 'EOF'
import os
os.remove('/important/file')
EOF
dcg 使用 tree-sitter 和 ast-grep 解析这些内联代码,找出其中的危险操作,而不只是做简单的字符串匹配。
50+ 可选安全规则包
默认保护之外,可以按需启用:
数据库:
toml
[packs]
"db.postgres" = true # DROP TABLE, TRUNCATE, DELETE without WHERE
"db.mysql" = true
"db.mongodb" = true
"db.redis" = true # FLUSHALL, FLUSHDB
"db.sqlite" = true
Kubernetes:
toml
"k8s.kubectl" = true # delete namespace, force delete pods
"k8s.helm" = true # helm delete, helm rollback
云服务:
toml
"cloud.aws" = true # s3 rm --recursive, ec2 terminate-instances
"cloud.gcp" = true
"cloud.azure" = true
Docker:
toml
"containers.docker" = true # docker rm -f, docker system prune -a
Terraform:
toml
"iac.terraform" = true # terraform destroy, terraform state rm
拦截示例:
bash
# db.redis 启用后会拦截:
redis-cli FLUSHALL # 清空整个 Redis 实例
redis-cli FLUSHDB # 清空当前数据库
三种绕过方式
dcg 不是无法绕过的,它的设计目标是"增加摩擦",而不是"完全阻止":
方式一:单命令绕过(临时)
bash
DCG_BYPASS=1 git reset --hard HEAD~5
环境变量只对这一次执行生效,下次同样的命令还是会被拦截。
方式二:一次性豁免
bash
dcg allow-once <code> # code 来自拦截提示里的授权码
适合需要执行一次特定操作的场景,不影响以后的拦截规则。
方式三:永久允许列表
bash
dcg allowlist add "git reset --hard HEAD" # 允许这个特定命令
dcg allowlist add "rm -rf ./dist" # 允许清理构建目录
写入配置文件,永久生效。
集成 Claude Code
bash
# 一键安装(easy-mode 自动配置 Claude Code 钩子)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/destructive_command_guard/main/install.sh?$(date +%s)" | bash -s -- --easy-mode
这会在 Claude Code 的配置里注册 PreToolUse 钩子,之后每次 Claude Code 准备执行命令时,都先经过 dcg 过滤。
手动配置(~/.claude/settings.json):
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "dcg"
}
]
}
]
}
}
CI 扫描模式
除了实时拦截,dcg 还有一个 scan 命令,用于扫描已提交到仓库里的危险命令:
bash
# 扫描当前仓库里的危险命令
dcg scan ./
# 扫描特定文件类型
dcg scan ./ --include "*.sh,*.yml,Dockerfile,Makefile"
扫描器理解执行上下文:
- Dockerfile 中的
RUN rm -rf /var/cache/*(合理,在构建层) - GitHub Actions workflow 中的
- run: kubectl delete namespace prod(值得关注) - Makefile 中的
clean: rm -rf ./dist(合理,明确意图) - shell 脚本中的
rm -rf $TMPDIR(需要检查 TMPDIR 是否有意外值)
项目地址与资源
- 🌟 GitHub : Dicklesworthstone/destructive_command_guard
- 📦 安装 :
install.sh --easy-mode
总结
Destructive Command Guard 解决的是 AI 编程工具普及带来的一个新风险:AI Agent 在执行任务时的操作范围和权限比以前的自动化工具大得多,它会真的运行 shell 命令、操作文件系统、执行 Git 操作。一个误判可能造成的损失,比以前随便运行一个命令严重得多。
dcg 的设计哲学是"增加摩擦而不是完全阻止":它不阻止你做任何事,只是在危险操作之前加了一个确认步骤。Fail-open 设计保证它自己不会成为故障点。上下文感知和 Heredoc 扫描保证误报率足够低,不会在你正常工作时不断打扰你。
50+ 安全规则包的覆盖面说明这个工具超越了"保护 rm -rf"这个朴素需求,把数据库、Kubernetes、云服务等生产级别的危险操作都纳入了防护范围。
对于已经在用 Claude Code 或类似工具做日常开发的工程师,装一个 dcg 的成本极低,提供的安全边际却值得拥有。
探索 PrimeSkills ------ 精选 AI Agent 与技能的市场,每一个都经过真实企业工作流验证,去掉浮夸,留下真正有用的。
欢迎访问我的个人主页,发现更多有价值的见解和有趣的产品。