Skip to content

跨平台沙箱 (Sandbox)

源码版本rust-v0.145.0

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。

職責

  1. PermissionProfile 決定是否啟用 sandbox 與選用哪類 backend:codex-rs/sandboxing/src/manager.rs:273-319
  2. 在 macOS 拼出 sandbox-exec 的 seatbelt policy 與 argv:codex-rs/sandboxing/src/seatbelt.rs:21-30
  3. 在 Linux 走 codex-linux-sandbox helper:先 no_new_privs + seccomp,再 exec bubblewrap:codex-rs/linux-sandbox/src/lib.rs:1-30
  4. Windows 用 restricted token / elevated backend 隔離檔案系統與網路:codex-rs/windows-sandbox-rs/src/lib.rs:13-50
  5. 在 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 都只看標籤,不關心平台細節。

關鍵檔案

SandboxType 把三種平台 backend 與「無沙箱」統一成一個標籤,core 只看標籤。

rust
pub enum SandboxType {
    None,
    MacosSeatbelt,
    LinuxSeccomp,
    WindowsRestrictedToken,
}

select_initial 裡,先靠 should_sandbox 決定要不要 sandbox,再去問平台能給什麼 backend:

rust
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 鏈路直接斷了。

rust
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"
            )
        }
    }
}

資料流

邊界與失敗

小結

SandboxManager 是 codex 跨平台 sandbox 的統一入口,把 PermissionProfile 翻譯成 SandboxType 再翻譯成帶 wrapper 的 argv。平台 backend 各自隔離,但都從同一份 FileSystemSandboxPolicy / NetworkSandboxPolicy 推導。要追「為什麼這條命令被拒」的現場,看 命令分類 execpolicy命令執行 exec-server 兩頁。