Codex 只读审查的权限边界:任务文件是谁写的?

一次 Codex 审查没有修改仓库代码,却留下了新的任务 JSON。看到这种现象,先别把它判成"只读沙箱失效"。更值得追问的是:文件由哪个进程写入,read-only 又设置在哪一层?
在 codex-plugin-cc 的同一份源码中,可以同时找到两种操作:审查线程向 Codex App Server 请求只读沙箱;插件自身用 Node.js 的文件接口保存任务。这两件事能够同时成立。模型任务的只读约束,不能直接推广成整个插件及其工具都没有写入能力。
本文核对的是官方仓库提交 db52e28f4d9ded852ab3942cea316258ae4ef346,核对日期为 2026-09-20。下面是源码路径分析,开头的文件变化是据此构造的排查情境,不是一次已经运行的审查实验。本文也不宣称发现了沙箱漏洞。^1^
把只读参数和写文件调用放在一起看

插件的 codex.mjs 在构造新线程参数时包含:
js
approvalPolicy: options.approvalPolicy ?? "never",
sandbox: options.sandbox ?? "read-only",
这两个字段被作为 App Server 线程参数使用。普通审查路径显式指定 read-only,反方审查路径也传入相同设置;一般任务则由 request.write 决定选择 workspace-write 还是 read-only。因此,判断任务权限时,应追到具体调用处,不能只看函数默认值。^2^^3^
与此同时,state.mjs 中的 writeJobFile 做的是:
js
ensureStateDir(cwd);
const jobFile = resolveJobFile(cwd, jobId);
fs.writeFileSync(jobFile, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
这里执行的是插件的 Node.js 文件操作,不是让 Codex 模型通过受控工具修改文件。codex-companion.mjs 的后台排队路径会调用它保存任务记录。源码还会更新 state.json。^4^^3^
关键差异不在"JSON 算不算写入",而在权限设置的对象:传给 Codex 线程的 sandbox 字段,没有把调用它的所有宿主代码自动变成同一沙箱内的操作。插件宿主仍受启动它的操作系统用户、父进程环境和外层限制约束;本文没有检查某台机器上的实际隔离配置,不能断言宿主拥有任意路径权限。
这也给排障提供了顺序:先确认写入者和路径,再讨论是否违反了那一层的约束。任务记录按预期更新,与模型是否改了被审查文件,是两个不同问题。
never 表示不弹审批,不是所有动作都获准

同一段参数里,approvalPolicy 和 sandbox 是两个独立字段。前者管理审批交互,后者指定任务的沙箱模式。
官方文档将 read-only 配合 never 列为非交互只读组合:不发起审批询问,仍在只读沙箱边界内工作。因此,不能把源码里的 never 翻译成"任何命令都能直接执行"。^5^
还有一个容易遗漏的实现细节:固定提交的 app-server.mjs 对服务端主动请求的默认处理,是返回 -32601 的 Unsupported server request 错误。它没有在这个处理函数中实现一个让用户点击同意的审批界面。^6^
据此可以得到一个有限但实用的判断:不能仅凭自己在交互式 Codex 中见过审批弹窗,就推断这个插件客户端也会完成同样的交互。 具体某个动作会被拒绝、返回错误还是不触发请求,仍由实际 Codex 版本、策略与调用路径决定;本文没有运行这些分支。
如果审查因为权限不足而失败,先保留原始错误,再确认是否确实需要那项操作。直接把任务改成全访问,会同时改变待验证的条件,无法证明原来只是缺少一个弹窗。
本地登录和配置从哪里进入这条链
官方 README 说明,插件使用环境中安装的全局 codex 二进制,复用本地 Codex CLI 的认证,并读取相应配置;项目级配置需在项目受信任时加载。^7^
直接连接路径提供了更具体的证据:SpawnedCodexAppServerClient 启动 codex app-server 时,传入工作目录,环境使用 this.options.env ?? process.env。这是一个继承进程环境的启动点,不是插件建立了另一套独立账号。^6^
但"复用配置"仍不能代替检查最终请求参数。前面已经看到,插件会为线程提供 approvalPolicy 和 sandbox;检查用户配置中的某个默认值,只能完成排查的一部分。
对团队来说,更实际的核对对象是:使用哪个 Codex 二进制和版本、以哪个工作目录启动、由哪个本地用户持有登录状态,以及具体命令最终请求哪种沙箱。核对时记录账号归属和配置来源即可,不要把认证文件内容复制到问题报告。
如果运行环境存在组织强制策略,实际允许值还要受这些约束影响。本稿只说明插件怎样提出请求,不把源码中的请求值当作服务器已经授予的全部有效权限。
MCP 要沿工具调用再核对一次

假设团队给 Codex 配置了工单 MCP,其中既有 get_ticket,也有 close_ticket。这是用于说明边界的假想工具组合,不代表本文已连接或测试某个服务。
审查线程请求 read-only,并不足以证明远端工单状态不会变化。判断 close_ticket 能不能执行,需要继续检查该工具是否对当前会话开放、工具审批策略如何设置,以及服务端凭据真正允许什么操作。
官方 MCP 文档提供了 enabled_tools、disabled_tools 和逐工具审批配置;禁用清单在启用清单之后应用。writes 模式依据工具是否标注为只读决定是否询问。^8^
这些是不同性质的控制。工具清单决定暴露什么,审批决定怎样批准一次调用,服务端权限决定凭据能够完成什么。工具描述里写着"只读",不能代替对服务端权限的核对;同样,仅从本地源码也不能推断某个远端调用必然成功。
若当前任务只需要读工单,一个容易核查的起点是仅开放必要读工具,并使用服务端本就不能改工单的凭据。需要写工具时,应单独验证其审批与拒绝路径。这里提出的是配置与验收方法,不是声称某段示例配置已经在所有版本的插件里通过。
用一份排查记录把权限落到具体操作

前面的差异可以用于处理开头的问题。下面是待填写的排查记录,观察栏必须来自实际机器;不要把"预期"当作已经发生的结果。
| 要核对的对象 | 应记录的证据 | 这项证据回答什么 |
|---|---|---|
| 插件与 Codex 版本 | 插件提交、Codex 版本、调用命令 | 是否在比较同一个实现 |
| 审查线程 | 实际线程参数或可追溯调用路径、错误输出 | 请求了什么约束,哪里未被满足 |
| 仓库文件变化 | 明确目标文件的前后哈希、差异与可获得的执行记录 | 哪些文件变了,能否归因于具体操作 |
| 插件任务文件 | 已确认的状态目录、文件更新时间与任务 ID | 是否属于宿主保存任务的路径 |
| 外部工具 | 当前会话工具清单、审批设置、服务端最小权限说明 | 远端副作用由什么控制 |
state.mjs 将状态根目录放在 CLAUDE_PLUGIN_DATA 下的 state 中;缺少该变量时回退到系统临时目录下的 codex-companion。子目录还结合工作区名称与规范化路径的哈希,因而不能简单把所有任务文件都归到一个固定仓库内目录。^4^
这份记录有两个用途。排障时,它帮助区分"任务没有修改代码""插件更新了本地状态""外部系统发生了变化",避免用一个"只读"标签解释所有现象。验收时,它迫使团队明确自己要限制的是哪项操作,再选择对应的控制。
需要进一步验证时,应在无敏感数据的隔离环境里做小范围测试:记录环境版本,准备一个可丢弃目标文件,观察预期禁止的修改是否被拒绝;外部工具则使用测试服务端与无写权限的测试凭据,独立验证拒绝结果。本文没有执行这些测试,也没有把它们计为通过。
文件最终没变,不能单独证明某次写入被沙箱阻止:也可能根本没有尝试。工单没关闭,也不能单独证明服务端权限有效:也可能调用没有到达服务端。记录必须包含与判断相匹配的执行证据,缺失时就保留为待验证。
接入团队前,先决定需要哪一种能力
如果需求只是给一小段差异提供第二意见,先采用不连接高权限外部工具的审查环境,更容易解释行为;如果确实需要修改仓库或操作外部系统,再分别开启并验证相关能力。
插件宿主自身能够执行文件操作,意味着来源审查和本地运行身份依然重要。把 Codex 线程设为只读,不能代替对插件代码的信任判断;把插件安装自官方仓库,也不能代替对本地配置和外部服务凭据的权限核对。这是由调用关系得出的工程判断,不是对插件安全性的全面评估。
这篇最终要留下的不是"能不能放心用"的笼统答案,而是一个可以逐项查证的结论:只读审查约束了特定任务;整个接入链还包括保存任务的宿主、已有登录配置,以及可能被调用的外部工具。 发生文件变化或权限错误时,把操作归到正确的执行者,才能找到真正需要调整的边界。
本文完成公开源码核对;未运行真实审查任务、沙箱越权测试或 MCP 副作用测试,不给出运行时安全通过结论。
Footnotes
-
线程参数与原生审查调用,
buildThreadParams、buildResumeParams、runAppServerReview。 ↩ -
App Server 客户端,
handleServerRequest、SpawnedCodexAppServerClient.initialize。 ↩ ↩2 -
官方 MCP 配置说明,2026-09-20 核对;工具可见性、审批和服务端权限须分别验证。 ↩