你按教程把 workspace-write 配好了,以为边界稳了。但去翻 openai/codex 这个月的 commit,会发现沙箱在补的不是功能,是洞:WSL 上借 wsl.exe 以 root 重进发行版、shell 快照把 broker 替换掉的假凭证还原成真的、macOS Seatbelt 里一条通配拒绝。给 macOS/WSL 上重度用 Codex 的人:旋钮之外,你得知道沙箱在防谁。
先说清楚一件事,因为这决定了你该不该动:这几个洞不在同一个发布里。其中两个已经进了 9 月 17 日的稳定版 rust-v0.155.0,另外三个还只存在于 0.156.0-alpha.*。很多人看到「alpha 修了安全问题」就想去升 alpha——方向反了。
审批是「问不问人」,沙箱是「批准之后还能碰什么」——两层的失败模式完全不同
这两层经常被当成一回事,但它们坏掉的样子差很远。
审批策略(approval_policy)决定的是:这一步要不要停下来问人。它失败的样子是你误批了一次危险操作——你走神了、你按了 y、你没细看那条命令。人看错了,但至少有人在看。
沙箱(sandbox_mode + 平台后端)决定的是:批准之后,这个进程在操作系统层面还能碰什么。它失败的样子是批准之后进程仍然越界——没人看得到,因为审批已经过了。Seatbelt、Landlock+seccomp、Restricted Token 在做的就是这个。
这个区别为什么重要?因为它解释了为什么「我审批很谨慎」不能替代读沙箱变更。你审批得再细,也审不出「workspace-write 在 WSL 上开网络时,interop 能起一个沙箱外的 Windows 进程」这种事——那不是一条命令长什么样的问题,是内核边界在哪的问题。本文接下来拆的四个洞,全部属于第二层。
一句话分界:审批是人的边界,沙箱是内核的边界。人会被说服,内核不会——前提是策略写对了。
WSL 上「开网络」曾经不等于「文件边界还在」
这是四个里已经进稳定版的两个之一,也是 WSL 用户最该回头看一眼的。
workspace-write 默认不开网络。为了跑 npm install、go mod download 这类活,几乎所有人都会在配置里打开它:
|
|
打开之后,你可能默认「文件边界还是 workspace-write 那套,只是多了一条网线」。PR #44286(Block WSL interop escapes from restricted filesystem sandboxes)的 Why 段说的不是这样:
With network access enabled, WSL interop can launch Windows processes outside the Linux filesystem sandbox, including using
wsl.exeto reenter the distribution as root.
翻译成工程语言:网络一开,WSL 的 interop 机制能让你从 Linux 文件沙箱里跳出去,起一个 Windows 进程;而这个 Windows 进程可以用 wsl.exe 重新进入发行版——以 root。绕的不是 Codex 的规则,是 Linux 沙箱这一层根本管不到 Windows 侧。
修法有三条,每条都值得看一眼,因为它暴露了边界的真实形状:
- 在 bubblewrap 里遮蔽 WSL interop socket(仅当文件写入受限时)——把逃逸通道本身挡掉。
- 在受限文件策略下拒绝
AF_VSOCKsocket 与io_uring,即使普通网络访问是允许的——AF_VSOCK是 VM 与宿主通信的通道,这条堵的是「经 VM socket 够到宿主服务」。 - 拒绝「受限文件系统 + 不受限网络」的旧版 Landlock 配置——当 WSL interop 目录存在时,这种组合直接不接受。
注意第二条的措辞:开了网络,不等于所有 socket 类型都放行。这是很多人对「网络访问」的隐含假设,而它不是事实。
落到决策上:如果你在 WSL 上、workspace-write、且开了 network_access = true,那么在这个修复落地之前,你的文件边界不是你以为的那条线。修复已经在 rust-v0.155.0(2026-09-17 发布) 里了,Release Notes 的 Bug Fixes 明确列着:
Blocked Windows-process escapes from restricted WSL sandboxes and hardened brokered shell snapshots against credential exposure. (#44286, #43909, #44040)
所以 WSL 用户的动作很简单:确认你的版本 ≥ 0.155.0。不用碰 alpha。
macOS 的 Seatbelt 这版收得比你以为的紧:一条通配拒绝,和一条被删掉的默认放行
这一节的两个改动还只在 alpha,稳定版没有。我先说清这个前提,再讲它紧在哪。
第一条:#46583 Deny XPC service lookups in macOS Seatbelt profiles。 这个 PR 的 diff 只有一行,加在 codex-rs/sandboxing/src/seatbelt.rs 里:
|
|
mach-lookup 是查 Mach 服务的动作,XPC 是 macOS 上进程间通信的主要方式。这里的关键是 xpc-service-name-prefix ""——空前缀匹配所有 XPC 服务名。不是封某个服务,是一刀切。
这一行的语义是「不许查任何 XPC 服务」。我把它写成语义直读,不写成结论:它在 alpha 里会不会拦坏某个依赖 XPC 的工具,需要你自己在 alpha 上实测,我没跑,不下断言。但如果你在 alpha 上遇到某个工具链报奇怪的错,这是第一个该看的地方。
第二条:#46532 Remove com.apple.runningboard from Seatbelt platform defaults。 diff 同样是一行删除,从 seatbelt_read_only_platform_defaults.sbpl 里拿掉:
|
|
runningboard 是 macOS 管进程生命周期和调度的服务。它原本躺在「平台默认放行」清单里——那份清单的作用是:在只读受限模式下,附一段 macOS 平台策略来保住常见工具链的兼容性(官方文档把这件事描述为「append a curated macOS platform policy instead of broadly allowing /System」)。现在它从默认放行里被拿掉了,意味着依赖 runningboard 的工具链在收紧后的策略下可能不再被默认许可。
两条合起来看,Seatbelt 这版的走向是:把「平台默认」这份宽容清单往回收,同时把 XPC 整类查询关掉。 这不是小修补,是策略姿态的变化。
排查顺序给你:如果你在 macOS 上、跑的是 alpha、升级后工具链报错——先查 XPC 通配拒绝(#46583),再查 runningboard(#46532),因为前者是一刀切、影响面更宽。稳定版用户不用动,这两个改动还没落地,等它进了稳定版再评估。
凭证与补丁:两个「看不见的扩权面」正在被收口
前两节讲的是「能碰到什么」,这节讲的是「以什么身份碰」和「权限被谁悄悄放大」。
第一个扩权面:shell 快照的凭证回灌。 #44040 Harden credential handling in shell snapshots and replay 的 Why 段写得很直白:
Shell quoting can hide credentials from raw-text checks, and startup files can restore real credentials after the broker replaces them with dummy values.
两层问题。第一层,shell 引用能把凭证藏过纯文本检查——你的密钥 export SECRET="abc",换个引法、转义一下,字符串扫描就看不见了。第二层更微妙:Codex 的 broker 会把凭证换成假值再交给沙箱里的进程,但启动文件(比如 .zshrc)能在替换之后把真凭证恢复回来。
修法是「不解值地解码 shell 字面量」——只解析字面量形态,不执行它,这样引号花招藏不住;并「拒绝含凭证的可执行源码,包括 alias、function、heredoc」。另外还补了:快照回放时防住经启动文件恢复凭证、以及 ENV 里的无关设置要保留(不能为了安全把环境搞坏)。
对你的影响:这是安全加固,正常用法透明。但如果你的 shell 启动文件里有 export 凭证的习惯,值得知道这条路径曾经存在。这个修复同样已经在 rust-v0.155.0 里(见上节引用的 Release Notes 同一行 #44040)。
第二个扩权面:apply_patch 会顺手扩父目录权限。 #39614 Prevent apply_patch from widening write permissions(merged 2026-08-20)的 Why 段:
Deriving permissions from the parent of an already-writable patch target can unnecessarily grant write access outside the intended workspace.
逻辑是:补丁工具在写一个文件前会去推导它需要的目录权限。如果目标本身已经在可写范围内,它还是去推了父目录权限——而父目录可能在工作区之外。修法是「对已被当前沙箱策略覆盖的目标,跳过推导额外父目录权限」,工作区之外的目标才继续请求父目录访问。
测试覆盖里有一条值得注意:「symlink 逃逸出工作区」的回归测试。符号链接把工作区内的路径指到外面,是这类权限推导最经典的绕过方式。
对你的影响:如果你自定义过 apply_patch 相关的写权限范围,值得复查一遍。这个修复 merged 于 8 月 20 日,早于 0.155.0。
顺带一个还没进稳定版的信任变化:codex-network-proxy 独立成二进制了。 #46573 新增了 codex-rs/network-proxy/src/main.rs,可以单独跑(codex-network-proxy --config <PATH>),默认起 HTTP 代理 127.0.0.1:3128 和 SOCKS5 127.0.0.1:8081。README 里有一段要读仔细:
HTTPS MITM is enabled automatically when
mode = "limited"or when MITM hooks are configured. The CA private key remains in proxy memory.
开 limited 模式就会自动启用 HTTPS 中间人,CA 私钥留在代理内存里,公开的 CA 文件放到 $CODEX_HOME/proxy/ 供子进程信任。这不是漏洞——它是本地策略执行的必然代价,你要做域名白名单就得能看见明文。但你得知道自己开了什么:开了 limited,你就是在信任一个跑在本机的中间人。 另一个细节,allow_local_binding 省略时「MXC 为 true,其它为 false」(#46523),即托管环境默认放行本地绑定,这个默认值是在 alpha 里翻的。
你是哪类用户,这四处里该在意哪个
四个洞拆完,回到最开始那个分界:这些全是沙箱层的边界变更,审批层看不见它们。 所以你的动作不是「把审批调严一点」,而是按平台对着查。
先说最容易被带偏的一点:不要为了这几个修复去升 alpha。 alpha 一天发好几个 tag(9 月 19 日一天就有 alpha.5/6/7/8 四个),跟着它追安全修复,等于把 alpha 的回归风险一起买进来。这不是假设——稳定版 rust-v0.155.1(9 月 18 日)修的就是 alpha 里带出来的回归:
New local TUI sessions now leave reasoning summaries disabled by default, fixing request rejection by providers that do not support them. (#46467)
一个默认值改动就能让请求被拒。正确姿势是在稳定版里等这些修复落地,而不是为了几个洞去用 alpha。
按平台对号入座:
图注:按 PR 号、改动文件、适用平台与发布状态对照六处边界变更(正文按主题归为四类:WSL interop、凭证快照、macOS Seatbelt 两条、网络代理);绿色为已进稳定版,橙色为仅 alpha。数据来源:openai/codex 各 PR/commit 与 network-proxy README,核验日 2026-09-20。
| 你的情况 | 该在意哪个 | 动作 |
|---|---|---|
WSL + workspace-write + 开了网络 |
#44286(interop 逃逸) |
确认版本 ≥ 0.155.0,已修,不用动配置 |
| macOS,跑稳定版 | #46583 / #46532 |
还只在 alpha,不用动,等落地 |
| macOS,已在 alpha | #46583(XPC 通配拒绝,影响面最宽) |
工具链报错先查这条,再查 #46532 |
| 启动文件里 export 凭证 | #44040 |
已进 0.155.0;考虑改用 broker/密钥管理,别长期写在 .zshrc |
自定义过 apply_patch 写权限 |
#39614 |
复查权限范围,注意 symlink 指向工作区外的场景 |
| 只用只读分析、不开网络、不在 WSL | 四个里三个与你无关 | 读一眼即可,别为此改配置 |
一份 alpha 升级判断清单(真要动的时候用):
- 锁版本号。 一天多个 tag,别追 latest。
- 看
prerelease字段。0.156.0-alpha.8是prerelease: true,API 里就能查,不用猜。 - body 为空时改看 commit 列表。 这个 tag 的 release body 只有一行
Release 0.156.0-alpha.8,真实内容全在 commit 里。对比范围是rust-v0.155.0...rust-v0.156.0-alpha.8(434 commits、300 个文件)——注意这是从上一个稳定版起的全量,不是「本版新增功能」,别把 commit 数当功能量。 - 只 grep 边界相关的目录,别通读:
|
|
关键词锁定:sandbox / seatbelt / landlock / bwrap / credential / escape。这四个洞全是这么捞出来的。
配置复查三问,今天就能答:
- 我在 WSL 上开了网络吗?版本是不是 ≥ 0.155.0?
- 我的 shell 启动文件里有没有
export凭证? - 我有没有自定义过
apply_patch的写权限范围?
最后说清本文的边界。 上面所有断言来自 commit message 与源码 diff,没有做运行时验证——我没在 alpha 上跑过 Seatbelt 那两条改动,所以「会不会拦坏某个工具」我不断言,只给排查顺序。434 commits 是从 0.155.0 到 alpha.8 的全量,不代表本版功能量。还有一类人本文基本不适用:只做只读分析、从不开网络、也不在 WSL 上跑的——这四个洞三个跟你无关,读完知道有这么回事就行。
真正的动作只有一个:去 codex --version 看一眼,WSL 用户确认 ≥ 0.155.0,其他人别动。 至于「沙箱这一版在防谁」,把这几个目录的 commit log 加进你的季度巡检,比读任何一篇教程都管用。
每条断言对应哪个 commit
openai/codexReleaserust-v0.156.0-alpha.8(prerelease: true,published 2026-09-19T17:20:24Z,body 仅一行)— https://github.com/openai/codex/releases/tag/rust-v0.156.0-alpha.8 ,访问 2026-09-20openai/codexReleaserust-v0.155.0(published 2026-09-17T23:14:43Z;Bug Fixes 含#44286, #43909, #44040)— https://github.com/openai/codex/releases/tag/rust-v0.155.0 ,访问 2026-09-20openai/codexReleaserust-v0.155.1(published 2026-09-18T20:03:04Z;#46467reasoning summary 默认值回归热修)— https://github.com/openai/codex/releases/tag/rust-v0.155.1 ,访问 2026-09-20- PR
#44286 Block WSL interop escapes from restricted filesystem sandboxes(merged 2026-09-09T19:43:56Z,改 5 文件)— https://github.com/openai/codex/pull/44286 ,访问 2026-09-20 - PR
#46583 Deny XPC service lookups in macOS Seatbelt profiles(merged 2026-09-19T03:03:54Z;diff 新增(deny mach-lookup (xpc-service-name-prefix ""))于codex-rs/sandboxing/src/seatbelt.rs)— https://github.com/openai/codex/pull/46583 ,访问 2026-09-20 - PR
#46532 Remove com.apple.runningboard from Seatbelt platform defaults(merged 2026-09-19T00:20:16Z;从seatbelt_read_only_platform_defaults.sbpl删除该行)— https://github.com/openai/codex/pull/46532 ,访问 2026-09-20 - PR
#44040 Harden credential handling in shell snapshots and replay(merged 2026-09-09T04:00:23Z,改 21 文件)— https://github.com/openai/codex/pull/44040 ,访问 2026-09-20 - PR
#39614 Prevent apply_patch from widening write permissions(merged 2026-08-20T06:23:27Z,改 3 文件)— https://github.com/openai/codex/pull/39614 ,访问 2026-09-20 - PR
#46573 Add a standalone network proxy binary with JSON configuration(merged 2026-09-19T02:09:19Z)— https://github.com/openai/codex/pull/46573 ,访问 2026-09-20 - PR
#46523 Default local binding to true for MXC managed networking(merged 2026-09-19T00:00:15Z)— https://github.com/openai/codex/pull/46523 ,访问 2026-09-20 codex-rs/network-proxy/README.md(MITM 自动启用、CA 私钥驻内存、端口默认值、allow_local_binding默认)— https://github.com/openai/codex/blob/main/codex-rs/network-proxy/README.md ,访问 2026-09-20- 仓库数据:star 125,307 / fork 19,449,Apache-2.0,描述
Lightweight coding agent that runs in your terminal(GitHub API 核验,2026-09-20) - 对比范围:
compare/rust-v0.155.0...rust-v0.156.0-alpha.8= 434 commits / 300 files(GitHub API 核验,2026-09-20) - 官方权限文档(Seatbelt 平台策略的
curated platform policy描述、codex debug/codex sandbox入口)— https://www.codex-docs.com/en/docs/agent-approvals-security ,访问 2026-09-20