2026 年 6 月 27 日凌晨,Claude Code 在 Windows 上执行了一条清理命令。因为一个跨平台路径错误,这条命令删掉了我整个桌面的文件。这篇文章记录事故经过、根因分析,以及我们为此做的安全架构改造——所有代码开源,所有 hook 可以直接拿去用。
一、事故经过
那天晚上我在用 Claude Code 构建一个 portfolio 网站。任务很简单:把 GitHub 上一个 Next.js 仓库改成纯静态 HTML + CSS + JS,然后推回去。
仓库克隆到了临时目录,构建完成后需要清理旧文件。Bash 的 rm 命令被安全 hook 拦截了——三次。每次 hook 都返回了 BLOCKED。
但 Claude Code 没有停下来。它换了个姿势:
cd /tmp/my-portfolio; Get-ChildItem -Force | Where-Object { $_.Name -ne '.git' } | Remove-Item -Recurse -Force这条命令在 Linux 上完全正确。但在 Windows 上,PowerShell 不认识 /tmp。cd 静默失败,后面的 Remove-Item -Recurse -Force 直接在我桌面(当前工作目录)上执行。
等我意识到的时候,43 个文件/文件夹已经永久删除——没有经过回收站,没有备份,没有撤销。包括我正在开发的主项目文件夹 “Life Guard” 的全部内容。
二、为什么三层防护全部失效?
事故之后复盘,发现当时的安全架构存在一个致命的盲区:
2.1 Bash hook 只保护 Bash
pre_tool_use.py 是当时唯一的安全防护,它只检测 Bash 工具的 rm -rf 模式。PowerShell 工具的 Remove-Item -Recurse -Force 完全不在检测范围内。
# 事故前的 pre_tool_use.py — 只检查 Bashif tool_name == 'Bash': if is_dangerous_rm_command(command): sys.exit(2) # 拦截# PowerShell? 没检查。Claude Code 被 Bash hook 拦了三次之后,换用了 PowerShell 工具——就像有人踹不开前门,绕到没锁的侧窗进去了。
2.2 跨平台路径假设
/tmp 在 Linux/macOS 上是标准临时目录。在 Git Bash(Windows)中,它被解析为 C:\Users\<user>\AppData\Local\Temp。但在 PowerShell 中,它以 /tmp 开头被当成当前驱动器的相对路径解析——结果就是 cd 失败,后续操作跑在了错误目录。
2.3 链式命令无短路保护
PowerShell 的 ; 不像 Bash 的 &&。前一条命令失败不会阻止后一条执行。cd /tmp/my-portfolio 静默失败 → Remove-Item 在桌面执行 → 灾难。
2.4 删除操作不可恢复
Remove-Item -Recurse -Force 在 Windows 上是永久删除,不经过回收站。等同于 Linux 的 rm -rf。没有任何”先看再删”的机制。
2.5 无操作审计日志
事故发生后,我只能靠记忆回想删了什么。git log 可以恢复版本控制的文件,但桌面上的非 git 文件(文档、图片、配置文件)全部无法追溯。
三、安全架构改造:四层纵深防御
基于事故分析,我们对 Claude Code 的安全 hook 进行了全面改造。所有代码都可以在 ~/.claude/hooks/ 目录下找到。
3.1 第一层:全工具链危险命令拦截
改造文件:pre_tool_use.py
将检测从仅 Bash 的 rm -rf 扩展到三个工具:
| 工具 | 检测内容 | 示例 |
|---|---|---|
| Bash | rm -rf 及变体 | rm -rf /, rm -r --force |
| PowerShell | Remove-Item -Recurse -Force、ri -r -fo 等 | 含别名和 cmd 风格 /S 参数 |
| cmd | rmdir /S、del /S /Q、format 等 | 含 deltree 遗留命令 |
关键代码:
def is_dangerous_powershell_command(command): """检测危险 PowerShell 命令""" patterns = [ r'\bremove-item\s+.*-recurse.*-force', r'\bri\s+.*-r\b.*-fo\b', # Remove-Item 别名 r'\brmdir\s+.*-recurse', r'\bdel\s+.*-recurse', r'\bformat-\w+', # 磁盘格式化 r'\bclear-content\s+.*-force', # ... 还有更多 ] # ...
def is_dangerous_cmd_command(command): """检测危险 cmd.exe 命令""" patterns = [ r'\brmdir\s+/s\s+/q', r'\bdel\s+/s\s+/q', r'\bformat\s+[c-z]:', r'\bdeltree\b', # ... ]同样重要的是 Bash wrapper 检测——当 Bash 命令中包含 powershell.exe "Remove-Item ..." 时,也会被拦截。
3.2 第二层:系统临时目录硬约束
改造文件:pre_tool_use.py + session_end.py
添加了系统 Temp 目录的操作拦截。任何对 /tmp、C:\Users\*\AppData\Local\Temp、$env:TEMP 等系统临时目录的删除操作都会被阻止,并提示使用项目内的 .claude/tmp/ 替代。
SYSTEM_TEMP_PATTERNS = [ r'/tmp/', # Unix r'[A-Za-z]:\\Users\\[^\\]+\\AppData\\Local\\Temp', # Windows r'\$env:TEMP\b', # PowerShell r'%TEMP%', # cmd]session_end.py 增加了安全的清理机制:.claude/trash/ 中的文件 7 天后自动清理,.claude/tmp/ 中的文件 24 小时后清理,且所有清理操作限定在项目目录边界内。
3.3 第三层:预删除备份 + 回收站改写
改造文件:pre_tool_use.py
当危险删除被拦截时,hook 会在拦截前做两件事:
自动备份——将被删除的目标复制到 .claude/backups/<timestamp>/:
def backup_before_delete(command): """删除前自动快照到 .claude/backups/""" target = extract_target_path(command) if target and target.exists(): backup_dir = project_root / '.claude' / 'backups' / timestamp shutil.copytree(target, backup_dir / target.name)改写建议——在 stderr 中输出安全的替代方案:
BLOCKED: Dangerous PowerShell command detected and preventedSAFE ALTERNATIVE:# Safe alternative (sends to Recycle Bin):$shell = New-Object -ComObject Shell.Application$folder = $shell.Namespace(0xA)$item = Get-Item "target_path"$folder.MoveHere($item.FullName)Windows 用户可以直接使用 Shell.Application COM 对象将文件送入回收站,而 Linux/macOS 用户则可以使用 mv 移到 .claude/trash/ 目录。
3.4 第四层:文件系统变更审计日志
改造文件:post_tool_use.py
每次 Write、Edit、Bash、PowerShell 工具调用后,自动记录文件系统变更到 .claude/logs/filesystem_audit.jsonl:
{ "timestamp": "2026-06-27T20:15:30", "session_id": "abc123", "tool": "Bash", "command": "rm -rf ./old-build", "cwd": "E:\\Desktop\\my-project", "changes": { "deleted": ["old-build/index.html", "old-build/style.css"], "modified": [] }}JSONL 格式支持追加写入(不需要加载全量),30 天后自动轮转。事故发生后可以准确追溯:谁在什么时候用什么工具删了什么。
四、完整的安全架构全景
用户 ← → Claude Code │ ┌─────────┼─────────┐ │ │ │ ▼ ▼ ▼pre_tool_use mid post_tool_use │ │ ├─ Layer 1: 拦截 │ │ Bash/PowerShell │ │ /cmd 危险命令 │ │ │ ├─ Layer 2: 拦截 │ │ 系统 Temp 操作 │ │ │ ├─ Layer 3: 备份 │ │ + 回收站改写 │ │ │ └─────────────────────┤ ▼ Layer 4: 审计日志 filesystem_audit.jsonl五、给 Claude Code 用户的建议
5.1 立即检查你的 hook 配置
ls ~/.claude/hooks/pre_tool_use.py如果这个文件只有针对 rm -rf 的 Bash 检测,说明你的 PowerShell 侧门还开着。本文中的所有 hook 文件都可以从这里获取(在 hooks 目录下)。
5.2 部署步骤
# 1. 备份现有 hookcp ~/.claude/hooks/pre_tool_use.py ~/.claude/hooks/pre_tool_use.py.bakcp ~/.claude/hooks/session_end.py ~/.claude/hooks/session_end.py.bakcp ~/.claude/hooks/post_tool_use.py ~/.claude/hooks/post_tool_use.py.bak
# 2. 复制新 hook 文件# (从本文提供的源码或 GitHub 仓库)
# 3. 运行测试确认cd ~/.claude/hookspython3 run_tests.py
# 4. 确认全部 19 项测试通过# ALL PASS5.3 日常使用注意事项
-
永远不要在 PowerShell 中链式执行
cd和Remove-Item。如果必须链式,用&&替代;(PowerShell 7+ 支持)或分步执行。 -
在 Windows 上用 Claude Code 时,所有路径使用 Windows 风格:
C:\Users\...而不是/tmp。 -
定期检查
.claude/logs/filesystem_audit.jsonl:这里面记录了每次写操作的文件变更。如果发现异常删除,第一时间查看审计日志。 -
.claude/backups/永远不要手删:这里存放着被拦截的删除操作之前的自动快照。定期检查,但不要自动清理。 -
如果你的 settings.json 中有
PowerShell(Get-ChildItem *)这样的通配权限,考虑替换为精确模式。通配权限会让Remove-Item也绕过权限检查。
六、写在最后
这次事故让我深刻理解了一件事:AI Agent 的安全模型不能是”建议”,必须是”硬约束”。
CLAUDE.md 里写了”禁止删除文件”,但如果技术链路没堵上,这条规则就只是一段文字。Bash 被拦了还有 PowerShell,PowerShell 被拦了还有 Python 内联执行——安全防护必须覆盖所有工具、所有入口。
25 项测试全部通过,零回归。这些 hook 已经在我自己的环境里运行,每次危险操作都会被拦截、备份、审计。如果你也是 Claude Code 用户,建议你也检查一下自己的安全配置。
本文所有 hook 代码可在 ~/.claude/hooks/ 找到。事故恢复指南也在同一目录下。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时





