跨平台沙箱 (Sandbox)
codex 把命令执行隔离到平台原生 sandbox 里:macOS 用 sandbox-exec (seatbelt)、Linux 用 bubblewrap + seccomp、Windows 用 restricted token。sandboxing crate 统一抽象,linux-sandbox/、bwrap/、windows-sandbox-rs/、process-hardening/ 是各平台实现。core 在每次 exec 前算出 SandboxType,再交给 SandboxManager::transform 拼出真正要 spawn 的 argv。
职责
- 按
PermissionProfile决定是否启用 sandbox 与选用哪类 backend:codex-rs/sandboxing/src/manager.rs:273-319 - 在 macOS 拼出
sandbox-exec的 seatbelt policy 与 argv:codex-rs/sandboxing/src/seatbelt.rs:21-30 - 在 Linux 走
codex-linux-sandboxhelper:先 no_new_privs + seccomp,再 exec bubblewrap:codex-rs/linux-sandbox/src/lib.rs:1-30 - Windows 用 restricted token / elevated backend 隔离文件系统与网络:
codex-rs/windows-sandbox-rs/src/lib.rs:13-50 - 在 main 之前做进程级加固(关 core dump、拒 ptrace、清
LD_PRELOAD/DYLD_*):codex-rs/process-hardening/src/lib.rs:12-25
设计动机
为什么不直接信任 execpolicy 的拦截?因为模型可以构造任意 shell,规则引擎永远会有边界 case。强制把每条命令关进 OS 原生 sandbox,是"模型再怎么绕也能在内核层挡住"的兜底。SandboxablePreference::Auto 让 workspace-write / read-only 等策略性 profile 启用 sandbox,而 full-access profile 直接退化为 SandboxType::None,避免无谓的进程包装。
各平台 backend 不同,是因为没有跨平台统一的 sandbox 原语:macOS 的 seatbelt 是声明式 .sbpl、Linux 的 bubblewrap 是 user namespace + bind mount、Windows 的 restricted token 是 token + ACL。SandboxType enum 把这三种 backend 与 None 统一成同一个标签,core/exec-server 都只看标签,不关心平台细节。
关键文件
codex-rs/sandboxing/src/lib.rs:1-46— 模块入口,按target_os决定编译哪个 backend。codex-rs/sandboxing/src/manager.rs:35-75—SandboxTypeenum 与get_platform_sandbox。codex-rs/sandboxing/src/manager.rs:273-329—SandboxManager与select_initial/should_sandbox。codex-rs/sandboxing/src/manager.rs:321-413—transform按 sandbox 类型把原始 argv 变成带 wrapper 的 argv。codex-rs/sandboxing/src/seatbelt.rs:21-30— macOSsandbox-exec路径写死/usr/bin/sandbox-exec,防 PATH 注入。codex-rs/linux-sandbox/src/launcher.rs:36-67— Linux bubblewrap launcher:优先系统 bwrap,回退 bundled。codex-rs/process-hardening/src/lib.rs:43-61— Linux 进程加固:prctl(PR_SET_DUMPABLE, 0)+ 清LD_*。codex-rs/core/src/exec.rs:117-130— core 侧调用select_initial的入口。
SandboxType 把三种平台 backend 与"无沙箱"统一成一个标签,core 只看标签。
pub enum SandboxType {
None,
MacosSeatbelt,
LinuxSeccomp,
WindowsRestrictedToken,
}在 select_initial 里,先靠 should_sandbox 决定要不要 sandbox,再去问平台能给什么 backend:
pub fn select_initial(
&self,
file_system_policy: &FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
pref: SandboxablePreference,
windows_sandbox_level: WindowsSandboxLevel,
has_managed_network_requirements: bool,
) -> SandboxType {
if self.should_sandbox(
file_system_policy,
network_policy,
pref,
has_managed_network_requirements,
) {
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
.unwrap_or(SandboxType::None)
} else {
SandboxType::None
}
}Linux launcher 在系统 bwrap 与 bundled bwrap 之间挑一个,找不到就 panic——bubblewrap 不可用时整个 Linux sandbox 链路直接断了。
pub(crate) fn exec_bwrap(argv: Vec<String>, preserved_files: Vec<File>) -> ! {
match preferred_bwrap_launcher() {
BubblewrapLauncher::System(launcher) => {
exec_system_bwrap(&launcher.program, argv, preserved_files)
}
BubblewrapLauncher::Bundled(launcher) => launcher.exec(argv, preserved_files),
BubblewrapLauncher::Unavailable => {
panic!(
"bubblewrap is unavailable: no system bwrap was found on PATH and no bundled \
codex-resources/bwrap binary was found next to the Codex executable"
)
}
}
}数据流
边界与失败
SandboxablePreference::Forbid强制关 sandbox,Require强制开,只有Auto才看 policy:codex-rs/sandboxing/src/manager.rs:303-319- macOS 上
sandbox-exec路径写死/usr/bin/sandbox-exec,绕开 PATH 攻击:codex-rs/sandboxing/src/seatbelt.rs:26-30 - Linux 上 bubblewrap 找不到时直接 panic 而非静默降级——避免"以为有沙箱实际没有"的不安全状态:
codex-rs/linux-sandbox/src/launcher.rs:42-48 - MITM CA 信任 bundle 路径会被显式加入 readable roots,否则 sandbox 内进程无法验证 TLS:
codex-rs/sandboxing/src/manager.rs:76-95 core::exec里is_likely_sandbox_denied在 exec 完成后看 stderr 判断是不是被 sandbox 拦了,给模型一个可读错误:codex-rs/core/src/exec.rs:768-810
小结
SandboxManager 是 codex 跨平台 sandbox 的统一入口,把 PermissionProfile 翻译成 SandboxType 再翻译成带 wrapper 的 argv。平台 backend 各自隔离,但都从同一份 FileSystemSandboxPolicy / NetworkSandboxPolicy 推导。要追"为什么这条命令被拒"的现场,看 命令分类 execpolicy 与 命令执行 exec-server 两页。