@admin镇馆龙发布于 10月11日 11:24
你的 coding agent 钩子可能形同虚设:fail-open 陷阱与三行配置堵上
用 Claude Code、Codex、Cursor 的同学,很多人配过 PreToolUse 钩子:拦截 rm -rf、拦截生产数据库写入、拦截密钥外发。但有个扎心的问题——如果钩子脚本自己崩了、超时了、没启动,拦截还会生效吗?
答案是:默认不会。这叫 fail-open(失败即放行)。
实测:四家全是 fail-open
developersdigest 10 月 9 日更新了一份横向实测,结论很齐:
- Claude Code:钩子崩溃、超时、起不来,默认放行。
- Cursor:权限钩子输出 malformed 时会 fail-closed,但崩溃、超时、异常退出码默认放行。
- Codex:文档里压根没有 fail-closed 开关,文档化的失败路径都是“继续执行 tool call”。
- Gemini CLI:文档写明,退出码非 0 非 2 就是 warning,动作继续。
触发这篇实测的是 Claude Code 更新日志里的两行:v2.1.294 修了“写成自然语言指令的 prompt/agent 钩子会放行本该拦截的东西”;紧接着 v2.1.295(10 月 8 日)加了 onFailure: "block"——钩子起不来、超时、异常退出码,直接拦截。
抄作业:三步堵上
第一步(Claude Code v2.1.295+): 给每个承担安全职责的 command/HTTP 钩子加上:
{
"hooks": [
{
"type": "command",
"command": "你的守卫脚本",
"timeout": 10,
"onFailure": "block"
}
]
}
注意两点:一是配个短 timeout,别让钩子 hang 住整个流程;二是 prompt、agent、MCP tool 钩子不在 onFailure 覆盖范围,别拿它们当安全门。
第二步(Cursor): 安全钩子加 "failClosed": true。
第三步(Codex / Gemini CLI): 没有开关,就把钩子当“辅助提醒”,真正的边界交给 sandbox 和审批策略(approval policy)。另外一个通用兜底:守卫脚本自己把异常转成 exit code 2,别让它带着非预期退出码裸奔。
一句话总结
钩子是“门卫”,但门卫睡着了门就敞开,等于没门。安全策略的默认值应该是“门卫不在就锁门”。花五分钟检查下你的配置,onFailure / failClosed 配上,今晚睡个安稳觉。
来源:https://www.developersdigest.tech/blog/coding-agent-hooks-fail-open-vs-fail-closed-2026


