文章目录
- Claude
- Bash 危险命令防护配置
- 作用域:项目级 vs 全局级
- 方法 A:全局配置(推荐,所有项目生效)
- 文件分布
- 步骤 1:将检测脚本放到全局目录
- 步骤 2:在 `~/.claude/settings.json` 中注册 Hook
- 步骤 3:验证
- 方法 B:当前项目配置(已有)
- 文件分布
- 步骤 1:`.claude/settings.local.json` — 注册 Hook
- 步骤 2:`check-dangerous-bash.py` — 检测逻辑
- 步骤 3:Hook 交互协议
- 工作流程
- 拦截的危险模式
- 1. `cat >` — 直接覆盖写入文件
- 2. `rm -r` / `rm -rf` — 递归强制删除
- 3. `dd if=` — 磁盘/文件原始读写
- 4. `>` — Shell 覆盖重定向
- 使用说明
- 被拦截时如何处理
- 临时禁用
- 修改检测规则
Claude
Bash 危险命令防护配置
通过 Claude Code 的PreToolUseHook 机制,在每次执行 Bash 命令前自动检测危险模式,强制弹出权限确认提示,防止意外覆盖或删除文件。
作用域:项目级 vs 全局级
Claude Code 支持三个层级的配置文件,优先级从低到高:
| 层级 | 文件路径 | Git | 适用场景 |
|---|---|---|---|
| 全局 | ~/.claude/settings.json | N/A | 所有项目生效,个人偏好 |
| 项目 | .claude/settings.json | 提交 | 团队共享的 hook、权限 |
| 项目本地 | .claude/settings.local.json | gitignore | 个人项目级覆盖 |
本配置已同时部署到全局和当前项目,对所有项目生效。
方法 A:全局配置(推荐,所有项目生效)
文件分布
| 文件 | 作用 |
|---|---|
~/.claude/settings.json | 全局 Hook 注册 |
~/.claude/hooks/check-dangerous-bash.py | 全局检测脚本 |
步骤 1:将检测脚本放到全局目录
mkdir-p~/.claude/hooks将check-dangerous-bash.py复制到~/.claude/hooks/。
步骤 2:在~/.claude/settings.json中注册 Hook
在已有的全局配置中新增hooks字段(与其他字段平级):
{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"python \"$HOME/.claude/hooks/check-dangerous-bash.py\"","timeout":5,"statusMessage":"Checking command safety..."}]}]}}路径区别:全局配置使用
$HOME/.claude/hooks/...,不能用$CLAUDE_PROJECT_DIR(那是项目根目录)。
步骤 3:验证
在新终端中打开任意项目,执行cat > test.txt——应弹出权限确认提示。然后删除测试文件即可验证全局生效。
方法 B:当前项目配置(已有)
文件分布
| 文件 | 作用 |
|---|---|
.claude/settings.local.json | 当前项目 Hook 注册 |
.claude/hooks/check-dangerous-bash.py | 当前项目检测脚本 |
步骤 1:.claude/settings.local.json— 注册 Hook
{"hooks":{"PreToolUse":[{"matcher":"Bash","hooks":[{"type":"command","command":"python \"$CLAUDE_PROJECT_DIR/.claude/hooks/check-dangerous-bash.py\"","timeout":5,"statusMessage":"Checking command safety..."}]}]}}字段说明:
| 层级 | 字段 | 值 | 说明 |
|---|---|---|---|
| 根 | hooks | {} | 所有 hook 的容器 |
| → | PreToolUse | [] | 在工具执行之前触发(可拦截) |
| → → | matcher | "Bash" | 只匹配Bash工具调用 |
| → → → | type | "command" | 执行 Shell 命令(另有prompt/agent/http类型) |
| → → → | command | "python ..." | 调用 Python 检测脚本,$CLAUDE_PROJECT_DIR为项目根目录 |
| → → → | timeout | 5 | 超时 5 秒,防止脚本卡死阻塞 |
| → → → | statusMessage | "Checking..." | 执行时 spinner 旁显示的提示文字 |
Hook 事件类型选择:
| 事件 | 时机 | 能否拦截 |
|---|---|---|
PreToolUse✅ | 工具执行前 | 可拦截(本次选用) |
PostToolUse | 工具执行成功后 | 仅通知 |
PostToolUseFailure | 工具执行失败后 | 仅通知 |
Hook 执行方式选择:
| type | 原理 | 适用场景 |
|---|---|---|
command✅ | 执行 Shell/Python 脚本 | 规则明确,速度快,零成本(本次选用) |
prompt | 调用 LLM 判断 | 需要语义理解(如"这个命令是否危险"),有 API 费用 |
agent | 启动子 Agent | 需要多步骤验证 |
http | POST 到外部服务 | 企业级集中管控 |
步骤 2:check-dangerous-bash.py— 检测逻辑
""" PreToolUse hook: check Bash commands for destructive patterns. Reads tool call JSON from stdin, outputs hook result JSON to stdout. Blocked patterns: - cat > file (bypasses Write tool's "read first" protection) - rm -r / rm -rf (irreversible delete) - dd if=... (disk write) - > file (redirect overwrite, not append >>) """importsysimportjsonimportredefmain():# 1. 解析 stdin 中的 JSON,提取 Bash 命令字符串try:data=json.load(sys.stdin)exceptjson.JSONDecodeError:print("{}")# 解析失败 → 放行returncmd=data.get("tool_input",{}).get("command","")ifnotcmd:print("{}")# 空命令 → 放行return# 2. 按顺序匹配危险模式(命中即停,不继续检查)# cat > file — 绕过 Write 工具保护ifre.search(r'\bcat\s+>',cmd):warn("cat > file — overwrites without confirmation, bypasses Write tool")# rm -r / rm -rf — 不可逆删除elifre.search(r'\brm\s+-r',cmd):warn("rm -r — irreversible delete")# dd if= — 块设备写入elifre.search(r'\bdd\s+if=',cmd):warn("dd — can destroy disks")# > 覆盖重定向(排除 >> 追加、2>&1 合并)elif'>>'notincmdand'2>&1'notincmdand'>&2'notincmd \andre.search(r'(?<!>)>(?!>)\s*\S',cmd):warn("> overwrite redirect — bypasses Write tool")# 3. 安全命令 → 输出空 JSON,放行else:print("{}")defwarn(reason):"""输出拦截指令 JSON,强制弹出权限提示"""print(json.dumps({"continue":True,# 不阻断流程,改为弹窗"systemMessage":f"WARNING:{reason}. Permission required.","hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask",# "allow" | "deny" | "ask""permissionDecisionReason":reason}}))if__name__=="__main__":main()脚本关键设计决策:
| 决策 | 选择 | 原因 |
|---|---|---|
| 拦截方式 | permissionDecision: "ask" | 弹确认框而非硬阻断,真正需要的操作可以放行 |
| 匹配顺序 | if/elif链,命中即停 | 避免一条命令触发多条警告 |
排除>> | 跳过追加重定向 | >>追加到文件末尾,不覆盖内容,安全 |
排除2>&1 | 跳过 stderr 合并 | 只是合并输出流,不写入文件 |
排除>&2 | 跳过 stdout→stderr | 同上 |
| 解析失败 | 输出{}放行 | 宁可漏过也不误阻断(fail-open) |
| 脚本语言 | Python | 比 bash 正则更可靠,JSON 处理更安全 |
步骤 3:Hook 交互协议
stdin 输入(Claude Code → 脚本):
{"tool_name":"Bash","tool_input":{"command":"cat > test.txt","description":"...","timeout":120000}}stdout 输出 — 安全命令(脚本 → Claude Code):
{}输出空 JSON 对象 = 放行,不做任何干预。
stdout 输出 — 危险命令(脚本 → Claude Code):
{"continue":true,"systemMessage":"WARNING: cat > file — overwrites without confirmation, bypasses Write tool. Permission required.","hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"cat > file — overwrites without confirmation, bypasses Write tool"}}| 字段 | 作用 |
|---|---|
continue: true | 不中断会话流程 |
systemMessage | 在 UI 中显示的警告文字 |
permissionDecision: "ask" | 强制弹出权限确认对话框 |
permissionDecisionReason | 对话框中展示的拦截原因 |
工作流程
Bash 命令 → PreToolUse Hook → check-dangerous-bash.py │ ┌───────┴───────┐ │ 检测危险模式 │ └───────┬───────┘ 安全 │ 危险 {} │ { "permissionDecision": "ask" } │ │ │ 直接执行 │ 弹出权限确认 │ ┌────┴────┐ │ 允许 拒绝 │ 执行 不执行拦截的危险模式
1.cat >— 直接覆盖写入文件
cat>output.txtcat>/path/to/file命令作用:cat默认将标准输入的内容输出到终端。加上>重定向后,会将终端输入的内容直接覆盖写入目标文件,原有内容全部丢失。
为何危险:Claude Code 的Write/Edit工具有"必须先 Read 才能覆盖"的保护机制,但cat >是 Bash 原生命令,绕过了这道保护,可能静默覆盖重要文件。这正是本配置要解决的核心问题。
2.rm -r/rm -rf— 递归强制删除
rm-rdirectory/# 递归删除目录及其内容rm-rfdirectory/# 递归删除,强制模式(不提示确认)rm-rfnode_modules/# 常见场景:清理依赖目录命令作用:
rm(remove):删除文件或目录-r(recursive):递归删除目录及其所有子目录和文件-f(force):强制删除,忽略不存在的文件,不弹出确认提示
为何危险:一旦执行不可逆(不经过回收站),-f参数会让命令静默执行不提示,极易误删整个项目目录。
3.dd if=— 磁盘/文件原始读写
ddif=/dev/zeroof=disk.imgbs=1Mcount=100# 创建镜像文件ddif=backup.isoof=/dev/sdb# 写入U盘/磁盘命令作用:
dd(data duplicator):底层数据复制工具if=(input file):指定输入源of=(output file):指定输出目标bs=(block size):每次读写的块大小
为何危险:dd直接操作块设备,of=可以指向磁盘分区(如/dev/sda),错误的of=参数会彻底摧毁磁盘数据,连文件系统结构都被覆盖,无法通过常规手段恢复。
4.>— Shell 覆盖重定向
echo"hello">file.txt# 将 echo 输出覆盖写入文件some-command>result.log# 将命令输出覆盖写入文件>data.json# 清空文件内容(Bash 允许省略命令)命令作用:>是 Shell 的输出重定向操作符,将左侧命令的标准输出覆盖写入右侧文件。如果文件已存在,原有内容先被清空再写入新内容。
为何危险:与cat >同理,绕过了Write/Edit工具的保护。> important_file会在瞬间清空文件,且不会经过确认。
区分:
>>是追加(append)重定向,不覆盖原有内容,因此不被拦截。
使用说明
被拦截时如何处理
当命令被拦截时,Claude Code 会弹出权限确认提示,显示拦截原因。你有三个选择:
- Allow(允许):确认安全,放行执行
- Deny(拒绝):阻止执行
- Ask(询问):回到对话,手动确认或用其他方式完成
临时禁用
如需大量执行文件操作,可在对话中告知"暂时关闭 hook",我将通过修改配置临时禁用检测。
修改检测规则
编辑.claude/hooks/check-dangerous-bash.py,修改main()函数中的正则模式即可增删拦截规则。修改后下次 Bash 调用即刻生效,无需重启。