Sandbox multiplataforma
codex aísla la ejecución de comandos en el sandbox nativo de cada plataforma: en macOS usa sandbox-exec (seatbelt); en Linux, bubblewrap + seccomp; en Windows, restricted token. El crate sandboxing unifica la abstracción; linux-sandbox/, bwrap/, windows-sandbox-rs/ y process-hardening/ son las implementaciones por plataforma. core calcula el SandboxType antes de cada exec, y luego se lo pasa a SandboxManager::transform para que componga el argv que se va a spawnear de verdad.
Responsabilidades
- Decidir según
PermissionProfilesi se habilita sandbox y qué backend se elige:codex-rs/sandboxing/src/manager.rs:273-319 - Componer en macOS la policy seatbelt de
sandbox-execy el argv:codex-rs/sandboxing/src/seatbelt.rs:21-30 - En Linux, ir al helper
codex-linux-sandbox: primero no_new_privs + seccomp, y luego exec bubblewrap:codex-rs/linux-sandbox/src/lib.rs:1-30 - En Windows, aislar sistema de archivos y red con restricted token / elevated backend:
codex-rs/windows-sandbox-rs/src/lib.rs:13-50 - Endurecer el proceso antes de main (cerrar core dumps, rechazar ptrace, limpiar
LD_PRELOAD/DYLD_*):codex-rs/process-hardening/src/lib.rs:12-25
Motivación de diseño
¿Por qué no confiar directamente en el bloqueo de execpolicy? Porque el modelo puede construir shells arbitrarios y un motor de reglas siempre tendrá casos límite. Forzar que cada comando se meta en el sandbox nativo del OS es la red de seguridad: «por mucho que el modelo lo intente rodear, el kernel lo frena». SandboxablePreference::Auto hace que los profiles de política como workspace-write / read-only activen sandbox, mientras que el profile full-access degenera en SandboxType::None, evitando un wrap de proceso sin sentido.
Los backends por plataforma son distintos porque no hay un primitivo de sandbox unificado multiplataforma: el seatbelt de macOS es un .sbpl declarativo; bubblewrap en Linux es user namespace + bind mount; el restricted token de Windows es token + ACL. El enum SandboxType unifica esos tres backends y None en una sola etiqueta; core y exec-server solo miran la etiqueta, sin importarles los detalles de plataforma.
Archivos clave
codex-rs/sandboxing/src/lib.rs:1-46— entrada del módulo; segúntarget_osdecide qué backend compilar.codex-rs/sandboxing/src/manager.rs:35-75— enumSandboxTypeyget_platform_sandbox.codex-rs/sandboxing/src/manager.rs:273-329—SandboxManagerconselect_initial/should_sandbox.codex-rs/sandboxing/src/manager.rs:321-413—transformconvierte el argv original en un argv con wrapper según el tipo de sandbox.codex-rs/sandboxing/src/seatbelt.rs:21-30— la ruta desandbox-execen macOS está hardcodeada a/usr/bin/sandbox-execpara prevenir PATH injection.codex-rs/linux-sandbox/src/launcher.rs:36-67— Linux bubblewrap launcher: prefiere el bwrap del sistema y, si no, retrocede al bundled.codex-rs/process-hardening/src/lib.rs:43-61— endurecimiento del proceso en Linux:prctl(PR_SET_DUMPABLE, 0)+ limpieza deLD_*.codex-rs/core/src/exec.rs:117-130— entrada del lado core que llama aselect_initial.
SandboxType unifica los tres backends de plataforma y «sin sandbox» en una sola etiqueta; core solo la lee.
pub enum SandboxType {
None,
MacosSeatbelt,
LinuxSeccomp,
WindowsRestrictedToken,
}En select_initial, primero should_sandbox decide si hace falta sandbox, y luego se pregunta a la plataforma qué backend puede ofrecer:
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
}
}El launcher de Linux elige entre el bwrap del sistema y el bundled; si no encuentra ninguno, entra en panic: sin bubblewrap, toda la cadena de sandbox en Linux se rompe.
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"
)
}
}
}Flujo de datos
Bordes y fallos
SandboxablePreference::Forbidfuerza apagar sandbox,Requirelo fuerza a encender; soloAutomira la policy:codex-rs/sandboxing/src/manager.rs:303-319- En macOS la ruta de
sandbox-execestá hardcodeada a/usr/bin/sandbox-exec, evitando ataques de PATH:codex-rs/sandboxing/src/seatbelt.rs:26-30 - En Linux, si no encuentra bubblewrap, entra en panic en vez de degradar silenciosamente: evita el estado inseguro de «crees que hay sandbox pero no lo hay»
codex-rs/linux-sandbox/src/launcher.rs:42-48 - El bundle de confianza del CA MITM se añade explícitamente a las readable roots; de lo contrario, el proceso dentro del sandbox no podría validar TLS:
codex-rs/sandboxing/src/manager.rs:76-95 - En
core::exec,is_likely_sandbox_deniedexamina stderr tras el exec para detectar si lo bloqueó el sandbox y darle al modelo un error legible:codex-rs/core/src/exec.rs:768-810
Resumen
SandboxManager es la entrada unificada al sandbox multiplataforma de codex: traduce PermissionProfile en SandboxType y luego en un argv con wrapper. Los backends por plataforma se aíslan cada uno por su lado, pero todos derivan de la misma FileSystemSandboxPolicy / NetworkSandboxPolicy. Para rastrear «por qué este comando fue rechazado», consulta Clasificación de comandos (execpolicy) y Ejecución de comandos exec-server.