慕雪的小助手正在绞尽脑汁···
慕雪小助手的总结
DeepSeek & LongCat
本文部分内容由 AI 辅助生成,请结合实际情况判断参考。

Codex里拦住直接rm命令,给误删多上一道保险。

1.起因

最近用Codex写代码的时候,脑子里突然蹦出来一个问题:这玩意权限一给高了,哪天模型来一句rm -rf,把目录清了可咋办?

正常写代码时删个测试文件、临时目录其实挺常见的,但AI有时候为了完成任务,删起东西来也是一点不手软。等它跑完再看终端输出,那可真是欲哭无泪。

最开始想到的办法很朴素,给zsh加一句alias:

1
alias rm='rmtrash'

这招平时自己在终端里敲命令还行,但放到Codex这里就不太稳了。Codex跑的shell不一定会加载你的交互式配置,/bin/rm也能直接绕过去。只靠alias,多少有点看运气。


2. Codex的PreToolUse Hook

Codex自带Hooks机制,可以在工具调用前后跑一段固定脚本。这里要用的就是PreToolUse,它能在shell命令真正执行前看一眼命令内容,发现是rm就直接拒绝。

先在用户目录创建文件:

1
2
~/.codex/hooks.json
~/.codex/hooks/block_rm.py

2.1 配置hooks.json

把下面这段放进~/.codex/hooks.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"hooks": {
"PreToolUse": [
{
"matcher": "^Bash$",
"hooks": [
{
"type": "command",
"command": "/usr/bin/python3 /Users/你的用户名/.codex/hooks/block_rm.py",
"timeout": 5,
"statusMessage": "Checking rm command"
}
]
}
]
}
}

这里的matcher只匹配Codex的shell工具,其他工具不会被这个Hook影响。

2.2 写拦截脚本

接着创建~/.codex/hooks/block_rm.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
import json
import re
import sys

RM_COMMAND = re.compile(
r"(?:^|[\n;&|])\s*"
r"(?:(?:command|sudo(?:\s+-[A-Za-z]+)*|env(?:\s+[A-Za-z_][A-Za-z0-9_]*=[^\s]+)*)\s+)*"
r"\\?(?:/(?:usr/)?bin/)?rm(?=$|[\s;|&])"
)

payload = json.load(sys.stdin)
command = str(payload.get("tool_input", {}).get("command", ""))

if RM_COMMAND.search(command):
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rmtrash <path> to move files to the macOS Trash. Do not use rm or /bin/rm."
}
}))

拦住命令之后别只甩一句“禁用”,不然AI又得重新猜下一步该干啥。这里直接告诉它用rmtrash把目标丢进废纸篓,正常删除文件的流程还能继续跑。

最后给脚本加上执行权限:

1
chmod 700 ~/.codex/hooks/block_rm.py

重新打开Codex,第一次使用时如果出现Hook信任提示,确认这个脚本就是自己写的之后再选择信任。

3. 到底拦住了哪些东西?

这个脚本只干一件事:拦截顶层shell命令里真正执行的rm

下面这些都会被拒绝:

1
2
3
4
rm file.txt
rm -rf 临时目录
/bin/rm file.txt
sudo rm file.txt

普通命令不受影响:

1
2
ls -la
git status

可以把Hook理解成Codex执行命令前的门卫。模型把命令递出去,门卫看见rm,直接给它挡回来,命令就不会落到系统里。

4. 这玩意不是万能的

这里还是得说清楚,Hook看到的是Codex顶层发出的工具调用。

如果模型先写了一个脚本,再执行:

1
./cleanup.sh

cleanup.sh里面再去跑rm,这个Hook只看到了./cleanup.sh,看不到脚本内部的命令。同理,Python的shutil.rmtree()、Node.js的fs.rm()也不是这个Hook能处理的。

所以这个方案的目标不是给电脑装上绝对防删盾,而是先把最容易出现的直接rm误操作拦住。重要项目还是老老实实用Git提交、分支或者worktree,真出问题了才有地方回去。

The end

给Agent多加一道确定性的限制,有时候比在提示词里写十遍“不要删文件”管用得多。至少下次看到它想跑rm -rf,不会再只能干瞪眼了。