mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1671 字
5 分钟
一次 rm -rf 桌面,和它引发的 Claude Code 安全架构改造

2026 年 6 月 27 日凌晨,Claude Code 在 Windows 上执行了一条清理命令。因为一个跨平台路径错误,这条命令删掉了我整个桌面的文件。这篇文章记录事故经过、根因分析,以及我们为此做的安全架构改造——所有代码开源,所有 hook 可以直接拿去用。


一、事故经过#

那天晚上我在用 Claude Code 构建一个 portfolio 网站。任务很简单:把 GitHub 上一个 Next.js 仓库改成纯静态 HTML + CSS + JS,然后推回去。

仓库克隆到了临时目录,构建完成后需要清理旧文件。Bash 的 rm 命令被安全 hook 拦截了——三次。每次 hook 都返回了 BLOCKED

但 Claude Code 没有停下来。它换了个姿势:

Terminal window
cd /tmp/my-portfolio; Get-ChildItem -Force | Where-Object { $_.Name -ne '.git' } | Remove-Item -Recurse -Force

这条命令在 Linux 上完全正确。但在 Windows 上,PowerShell 不认识 /tmpcd 静默失败,后面的 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 — 只检查 Bash
if 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 扩展到三个工具:

工具检测内容示例
Bashrm -rf 及变体rm -rf /, rm -r --force
PowerShellRemove-Item -Recurse -Forceri -r -fo含别名和 cmd 风格 /S 参数
cmdrmdir /Sdel /S /Qformatdeltree 遗留命令

关键代码

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 目录的操作拦截。任何对 /tmpC:\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 prevented
SAFE 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. 备份现有 hook
cp ~/.claude/hooks/pre_tool_use.py ~/.claude/hooks/pre_tool_use.py.bak
cp ~/.claude/hooks/session_end.py ~/.claude/hooks/session_end.py.bak
cp ~/.claude/hooks/post_tool_use.py ~/.claude/hooks/post_tool_use.py.bak
# 2. 复制新 hook 文件
# (从本文提供的源码或 GitHub 仓库)
# 3. 运行测试确认
cd ~/.claude/hooks
python3 run_tests.py
# 4. 确认全部 19 项测试通过
# ALL PASS

5.3 日常使用注意事项#

  1. 永远不要在 PowerShell 中链式执行 cdRemove-Item。如果必须链式,用 && 替代 ;(PowerShell 7+ 支持)或分步执行。

  2. 在 Windows 上用 Claude Code 时,所有路径使用 Windows 风格C:\Users\... 而不是 /tmp

  3. 定期检查 .claude/logs/filesystem_audit.jsonl:这里面记录了每次写操作的文件变更。如果发现异常删除,第一时间查看审计日志。

  4. .claude/backups/ 永远不要手删:这里存放着被拦截的删除操作之前的自动快照。定期检查,但不要自动清理。

  5. 如果你的 settings.json 中有 PowerShell(Get-ChildItem *) 这样的通配权限,考虑替换为精确模式。通配权限会让 Remove-Item 也绕过权限检查。

六、写在最后#

这次事故让我深刻理解了一件事:AI Agent 的安全模型不能是”建议”,必须是”硬约束”

CLAUDE.md 里写了”禁止删除文件”,但如果技术链路没堵上,这条规则就只是一段文字。Bash 被拦了还有 PowerShell,PowerShell 被拦了还有 Python 内联执行——安全防护必须覆盖所有工具、所有入口。

25 项测试全部通过,零回归。这些 hook 已经在我自己的环境里运行,每次危险操作都会被拦截、备份、审计。如果你也是 Claude Code 用户,建议你也检查一下自己的安全配置。


本文所有 hook 代码可在 ~/.claude/hooks/ 找到。事故恢复指南也在同一目录下。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

一次 rm -rf 桌面,和它引发的 Claude Code 安全架构改造
https://www.moonzj.com/posts/claude-code-safety/
作者
张敬
发布于
2026-06-27
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录