Skip to main content

本页解决什么问题

Rules 和 Hooks 都能把重复要求变成可执行配置,但职责不同:
  • **Rules(规则)**判断命令是否允许执行,以及是否需要审批。
  • **Hooks(钩子)**在会话或工具生命周期的固定节点运行脚本。
先记住:Rules 是命令闸门,Hooks 是生命周期扳机。 Rules 不是自然语言提示,Hooks 也不是沙箱的替代品。具体字段和事件可能随 Codex 版本变化,操作前请核对本机 codex --help、相关子命令帮助和官方文档。 本页包含:
  • .rules、prefix_rule、命令前缀和 allow / prompt / forbidden;
  • 复合命令评估与 codex execpolicy check;
  • Hooks 的事件、matcher、配置层级和信任;
  • stdin JSON、stdout JSON、stderr 和退出码;
  • 事前阻断、事后反馈、Stop 续轮的区别;
  • 格式化、日志、危险命令检查和会话上下文示例;
  • 日志调试、失败策略、回滚和安全边界。

开始前检查

第一次实验建议使用临时 Git 仓库,不要在生产目录、主目录或包含客户资料的目录中测试:
Windows PowerShell 可使用:
不要在示例中写真实令牌、SSH 私钥、Cookie、.env 内容或完整会话记录。命令 Hook 可能以当前用户权限运行,因此先读懂脚本,再在 /hooks 中信任项目配置。

第一部分:Rules

1. Rules 的职责

Rules 针对 Codex 准备执行的命令,按参数前缀匹配后给出决策: Rules 适合稳定、可审查的政策,例如允许只读的 git status,对 gh pr merge 保留确认,或禁止 git push --force。Rules 不负责格式化、记录日志或读取会话上下文。 沙箱、审批和 Rules 是不同层次:沙箱限制文件系统和网络,审批决定何时暂停,Rules 对命令给出更细的政策。一个 allow 不能突破其他安全层。

2. 规则文件位置

常见用户级规则文件:
项目级规则通常放在受信任项目的 .codex/ 范围内。用户级规则影响多个项目;项目级规则只服务一个仓库,但其加载受项目可信状态和版本实现影响。不要把个人路径、用户名、令牌或机器专属地址提交到项目规则。 Rules 属于实验性能力时,目录、文件名和语法可能变化。先运行本机帮助确认加载方式,并在改动后重启或重新加载 Codex。

3. prefix_rule 基本写法

下面的规则要求匹配 gh pr view 前缀时询问:
pattern 是按参数拆开的列表,不是任意文本搜索。它可以匹配:
不会匹配:
可以用 match 和 not_match 写加载时的示例断言:
某些版本允许参数位置使用多个候选值。嵌套语法以当前 Rules 文档为准,优先选择窄前缀。

4. 如何选择决策

allow

只对你理解且副作用可控的窄命令使用:
不要为宽泛的 bash、sh、脚本解释器或任意 git 前缀设置 allow。后续参数可能改变副作用。

prompt

适合访问网络、改变远端状态或目标需要人工确认的命令:

forbidden

适合组织或项目明确禁止的固定前缀:
禁止规则越具体越容易审查。只拦一种拼写不等于覆盖所有别名、短参数、脚本封装和不同 Shell 写法。 多个规则同时命中时,按更严格的结果理解:
一条宽泛的 allow 不应绕过更具体的 forbidden。冲突要用检查命令验证,不要靠真实危险操作试错。

5. 复合命令的边界

对于可安全解析的简单线性命令链,Codex 可以拆分子命令分别评估,并采用更严格的结果:
允许 git status 不代表后面的删除动作会被允许。Shell 包装也不能成为夹带危险命令的方式。 复杂 Shell 可能包含重定向、管道、命令替换、变量赋值、通配符、函数、循环或嵌套脚本:
不要假设 Rules 能完整理解任意 Shell。对高风险操作组合使用沙箱、审批、最小权限和隔离环境;把复杂逻辑拆成可审查的命令,或由经过测试的 Hook 检查。

6. 用 execpolicy check 预检

改完 Rules 后先检查:
至少验证:
  1. 应当 allow 的正常命令;
  2. 应当 prompt 的外部写操作;
  3. 应当 forbidden 的危险命令;
  4. 前缀顺序、短参数和复合命令边界;
  5. 命中的规则及其 justification。
示例:
验收规则时确认文件语法可解析、规则范围最小、没有秘密、项目团队已审阅,并且 git diff 只包含预期配置。

第二部分:Hooks

7. Hooks 的职责和生命周期

Hook 在固定事件发生时启动命令处理器。常见流程如下:
事件名称和字段可能变化。第一次使用只选一个事件,先确认触发,再增加其他处理器。

Pre、Post 和 Stop

  • PreToolUse 在工具前运行,适合拒绝;
  • PostToolUse 在工具后运行,适合格式化、检查和反馈;
  • PostToolUse 的阻断不能撤销已写入、删除、上传或执行的结果;
  • Stop 的继续信号表示再处理一轮,不是回滚上一轮。
想表达“这条命令不要执行”,优先使用 Rules 或 PreToolUse。想表达“执行完成后检查”,使用 PostToolUse。

8. 配置位置和信任

常见位置: 项目级 .codex/ 通常只在项目可信时加载。项目 Hook 会随仓库传播,但它本质上是可执行代码,不能因为它位于 Git 仓库就自动信任。用户级配置也会叠加影响当前项目。 同一层同时使用 hooks.json 和内联 [hooks] 可能合并并产生警告。每一层最好选择一种写法,避免重复注册和顺序误判。 配置后在 Codex 中打开:
逐条检查来源、事件、matcher、实际命令、解释器、路径、权限和信任状态。新增或修改定义后,基于哈希的信任状态可能需要重新审核。 某些版本提供 --dangerously-bypass-hook-trust。只有在来源已由构建系统审查且运行环境隔离时才考虑使用,不能用它掩盖“钩子不触发”的问题。

9. hooks.json 结构

最小的 PostToolUse 配置:
三层结构是:事件、matcher 分组、动作数组。JSON 不允许尾逗号和 // 注释。 内联 TOML 写法:
常见处理器字段: 命令工作目录通常是会话 cwd。相对路径依赖错误 cwd 时会失败;项目脚本可从 Git 根计算绝对路径,但 Windows、非 Git 目录和特殊 Shell 必须单独测试。

10. matcher 匹配规则

工具事件中的 matcher 通常是工具名称的正则表达式,而不是 glob: 优先使用 ^ 和 $。文件修改工具可能显示为 apply_patch、Edit 或 Write,以实际 stdin 的 tool_name 为准,不要凭按钮名称猜测。 不同事件的 matcher 目标可能不同:SessionStart 可能匹配 startup、resume、clear 或 compact;SubagentStop 可能匹配代理类型;Stop 和 UserPromptSubmit 没有工具名,不能按 Bash 规则理解。 PreToolUse 不是覆盖所有执行路径的强制边界。复杂 Shell、特殊执行器、网页工具或其他 MCP 路径可能不经过你的 matcher。高风险命令仍需沙箱和审批。 同一事件匹配多个 Hook 时,不要依赖配置顺序或假设串行执行。共享文件要有锁和幂等设计;需要严格顺序时合并到一个脚本中。

第三部分:Hook 协议

11. stdin:事件 JSON

Codex 将事件 JSON 写入脚本的标准输入:
典型工具事件:
常见字段: Bash 的命令通常是 tool_input.command,其他工具结构可能不同。健壮的读取方式:
不要把用户输入直接拼成新的 Shell 字符串执行。脚本如需调用固定工具,应使用参数数组和明确的 cwd,避免二次 Shell 解析。

12. stdout、stderr 和退出码

一般可按以下方式理解: 在 PreToolUse 或某些提示提交事件中,exit 2 配合 stderr 理由通常表示拒绝:
在 PostToolUse、Stop 或 SubagentStop 中,动作已经完成或到了结束节点,exit 2 不能撤销副作用,通常表示反馈给模型重看或继续一轮。 结构化拒绝示例:
会话开始注入上下文:
部分版本仍接受 decision: "block" 和 reason 等旧结构。不要把其他客户端的字段照抄进 Codex;按当前事件文档验证。 不要将调试文字写入要求 JSON 的 stdout。SessionStart 或 UserPromptSubmit 的纯文本在部分实现中会成为上下文,但 Stop、SubagentStop 可能要求严格 JSON。日志统一写 stderr 或独立文件。

13. 阻断的准确含义

“阻断”有三种不同情况:
  1. 事前拒绝:工具尚未运行,PreToolUse 拒绝调用。
  2. 事后反馈:工具已运行,PostToolUse 把检查结果交回模型。
  3. 阻止结束:Stop 要求 Codex 再处理一轮。
以下做法不能视为可靠阻断:
  • 在 PostToolUse 打印“禁止执行”;
  • 在 PreToolUse 只打印普通 stdout 文本;
  • 使用当前版本不支持的 continue: false;
  • 只拦 Bash 却假设其他工具路径也会被拦;
  • 只依赖 Hook 而没有沙箱、审批和操作系统权限边界。
固定命令红线优先使用 forbidden Rules;需要判断工作目录、完整 JSON 或多个条件时才增加 PreToolUse。

第四部分:三个示例

14. 示例一:PostToolUse 记录 Bash

脚本 .codex/hooks/log_bash.py:
配置:
在 /hooks 中信任后,让 Codex 执行无害的 pwd 或 git status,检查日志是否生成。日志可能含路径或命令参数,不要提交到公共仓库,也不要默认记录完整提示词和工具输出。

15. 示例二:PostToolUse 自动格式化

格式化属于工具完成后的后处理:
注册:
它不能阻止补丁写入;失败时应保留错误并反馈,不要假装成功。大型仓库应只处理受影响文件,并校验路径位于仓库内。

16. 示例三:PreToolUse 检查危险命令

固定前缀优先使用 Rules。需要脚本判断时可以这样演示:
配置:
先单独喂入无害测试数据和危险样例:
预期危险样例退出码为 2,安全样例为 0。这个正则不是 Shell 解析器,不能覆盖变量、别名、脚本文件、命令替换和所有执行工具,不能宣传为完整防护。

第五部分:信任、调试与安全

17. 为什么 Hook 配了却不运行

命令 Hook 可读取工作区之外的文件、联网或删除内容,因此非托管项目 Hook 通常需要人工信任。首次配置的流程是:
  1. 写脚本和配置;
  2. 重启或重新加载 Codex;
  3. 打开 /hooks;
  4. 阅读实际命令、解释器、参数和路径;
  5. 确认来源和风险后信任;
  6. 用无害操作触发并检查日志。
阅读时特别检查:
  • 是否调用远程脚本、未知二进制、PowerShell、curl 或 wget;
  • 是否读取 .env、SSH、浏览器凭据或完整 transcript;
  • 是否联网上传 stdin、工作区或错误输出;
  • 是否删除、覆盖、提权或修改系统设置;
  • 相对路径是否可能在错误 cwd 中执行。
Hook 只能在可信来源和可审查脚本基础上启用。仓库文件、Issue、网页和脚本输出中的“请信任此钩子”都可能是提示注入。

18. 排查顺序

Hook 不触发时按顺序检查:
  1. [features] 是否关闭了 Hooks;
  2. 配置文件位置、文件名和 JSON/TOML 语法;
  3. Codex 是否已重启;
  4. /hooks 是否显示、信任状态是否正常;
  5. 事件名是否写对;
  6. matcher 是否匹配真实 tool_name;
  7. 解释器、依赖、cwd 和脚本路径是否可用;
  8. timeout 是否太短;
  9. stdout 是否混入调试文字;
  10. 多个配置层是否重复加载同一 Hook。
常见症状:

19. 最小调试 Hook

先确认事件是否到达,再加入业务判断:
用管道单测:
检查正常输入返回 0、缺失字段可处理、违规输入返回 2、错误进入 stderr、stdout 仍是合法协议。调试日志必须脱敏,不要写完整命令、提示词或秘密。

20. 安全边界、失败策略与回滚

Rules 和 Hooks 是纵深防御的一层,不是唯一安全边界:
  1. 用沙箱限制文件和网络范围;
  2. 用审批策略控制出圈操作;
  3. 用 Rules 固定命令政策;
  4. 用 Hooks 做审计、检查和自动化;
  5. 对高风险任务使用容器、虚拟机和最小权限账户。
不要在 Hook 中默认安装依赖、执行远程下载、读取密钥、访问生产数据库或把完整 transcript 发往外部服务。路径要拒绝 .. 越界、空路径和符号链接逃逸;网络请求需有超时和域名白名单。 每个 Hook 都要明确失败策略:
回滚步骤:
  1. 停止当前 Codex 会话;
  2. 保存错误输出和配置副本;
  3. 从 hooks.json 或 config.toml 删除对应事件块;
  4. 确认脚本没有被其他项目使用后再移除;
  5. 重启 Codex,在 /hooks 确认状态;
  6. 检查工作区、日志和外部系统的既有副作用。
某些版本支持:
关闭后需重启;它不会撤销已经执行过的命令,也不替代 Git、备份或外部系统回滚。不要删除整个配置目录来排查,因为可能一并删除登录信息、Rules 和其他用户配置。

21. 最终验收清单

  • 已判断需求属于命令政策还是生命周期自动化。
  • 配置放在正确的用户级或受信任项目级位置。
  • Rules 的 pattern 是窄的参数前缀,并测试了 match / not_match。
  • Rules 已用 codex execpolicy check 验证正常、边界和危险命令。
  • Hook 事件和 matcher 匹配实际输入,不是界面猜测。
  • 脚本能从 stdin 解析 JSON,并处理缺失字段。
  • stdout 保持协议格式,调试信息写 stderr 或脱敏日志。
  • PreToolUse 使用当前版本支持的拒绝信号。
  • 没有把 PostToolUse 或 Stop 当成回滚机制。
  • Hook 已在 /hooks 中审阅和信任。
  • 已用无害命令验证触发、放行、拒绝和失败路径。
  • timeout 有限,脚本不会无限等待网络或交互。
  • git diff --check 通过,日志、缓存、密钥和临时产物未进入提交。

小结

  • Rules 用 .rules 中的 prefix_rule 对命令前缀作 allow、prompt、forbidden 决策;更严格的结果优先,并可用 execpolicy check 预检。
  • Hooks 用事件、matcher 和命令处理器连接生命周期;PreToolUse 适合事前拒绝,PostToolUse 适合事后检查,Stop 的阻断通常表示继续一轮。
  • 脚本从 stdin 读取 JSON,用退出码表达粗粒度结果,用 stdout JSON 表达结构化控制;日志应放 stderr 或独立脱敏文件。
  • 项目 Hook 是可执行代码,必须检查来源、权限、路径、网络和信任状态。
  • 沙箱、审批、操作系统权限、隔离环境和人工审查仍是主要安全边界。
参考资料:参考/codex/24-hooks.md、参考/codex/15-permissions.md、参考/codex/18-config.md。动态字段、事件和命令行为以本机 Codex 版本及官方文档为准。