跨平台沙箱 (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 兩頁。