use crate::LinuxSandboxPidNamespace; #[cfg(target_os = "linux")] use crate::bwrap::WSL1_BWRAP_WARNING; #[cfg(target_os = "linux")] use crate::bwrap::is_wsl1; use crate::landlock::CODEX_LINUX_SANDBOX_ARG0; use crate::landlock::create_linux_sandbox_command_args_for_permission_profile; use crate::policy_transforms::effective_permission_profile; use crate::policy_transforms::should_require_platform_sandbox; #[cfg(target_os = "windows")] use crate::resolve_windows_elevated_filesystem_overrides; #[cfg(target_os = "windows")] use crate::resolve_windows_restricted_token_filesystem_overrides; #[cfg(target_os = "macos")] use crate::seatbelt::MacosSeatbeltProfile; #[cfg(target_os = "windows")] use crate::windows_sandbox_uses_elevated_backend; use codex_network_proxy::ManagedNetworkSandboxContext; use codex_network_proxy::NetworkProxy; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::AdditionalPermissionProfile; use codex_protocol::models::PermissionProfile; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::SandboxPolicy; pub use codex_protocol::sandbox::SandboxType; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; use std::collections::HashMap; use std::ffi::OsString; use std::io; use std::path::Path; #[cfg(target_os = "windows")] const WINDOWS_SANDBOX_WRAPPER_SETUP_ENV_ALLOWLIST: &[&str] = &[ "USERNAME", "USERPROFILE", // ShellExecuteExW needs SystemRoot to elevate the setup helper. "SYSTEMROOT", ]; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum SandboxablePreference { Auto, Require, Forbid, } pub fn get_platform_sandbox(windows_sandbox_enabled: bool) -> Option { if cfg!(target_os = "macos") { Some(SandboxType::MacosSeatbelt) } else if cfg!(target_os = "linux") { Some(SandboxType::LinuxSeccomp) } else if cfg!(target_os = "windows") { if windows_sandbox_enabled { Some(SandboxType::WindowsRestrictedToken) } else { None } } else { None } } pub fn with_managed_mitm_ca_readable_root( permission_profile: PermissionProfile, managed_mitm_ca_trust_bundle_path: Option<&AbsolutePathBuf>, sandbox_policy_cwd: &Path, ) -> PermissionProfile { let Some(managed_mitm_ca_trust_bundle_path) = managed_mitm_ca_trust_bundle_path else { return permission_profile; }; let (file_system_sandbox_policy, network_sandbox_policy) = permission_profile.to_runtime_permissions(); let file_system_sandbox_policy = file_system_sandbox_policy.with_additional_readable_roots( sandbox_policy_cwd, std::slice::from_ref(managed_mitm_ca_trust_bundle_path), ); PermissionProfile::from_runtime_permissions_with_enforcement( permission_profile.enforcement(), &file_system_sandbox_policy, network_sandbox_policy, ) } #[derive(Debug)] pub struct SandboxCommand { pub program: OsString, pub args: Vec, pub cwd: PathUri, pub env: HashMap, pub managed_network: Option, pub additional_permissions: Option, } /// A host-native launch request produced after [`SandboxManager::transform`] validates URI inputs. /// Build this only at the execution boundary: in exec-server, or in its logical equivalent within /// app-server. Orchestration and transport code should retain [`PathUri`] values and defer /// conversion to native paths until this request is created. #[derive(Debug)] pub struct SandboxExecRequest { pub command: Vec, pub cwd: PathUri, pub sandbox_policy_cwd: PathUri, pub env: HashMap, pub network: Option, pub network_environment_id: Option, pub sandbox: SandboxType, // TODO(anp): Reconcile these backend copies with the supplied sandbox context // (TurnEnvironment::sandbox_context for turns), preserving this launch snapshot. pub windows_sandbox_level: WindowsSandboxLevel, pub permission_profile: PermissionProfile, pub arg0: Option, } /// Bundled arguments for sandbox transformation. /// /// This keeps call sites self-documenting when several fields are optional. pub struct SandboxTransformRequest<'a> { pub command: SandboxCommand, pub permissions: &'a PermissionProfile, pub sandbox: SandboxType, pub enforce_managed_network: bool, pub environment_id: Option<&'a str>, // TODO(viyatb): Evaluate switching this to Option> // to make shared ownership explicit across runtime/sandbox plumbing. pub network: Option<&'a NetworkProxy>, pub sandbox_policy_cwd: &'a PathUri, pub sandbox_exe: Option<&'a Path>, // TODO(anp): Reconcile these backend inputs with the supplied sandbox context // (TurnEnvironment::sandbox_context for turns) so selection shares its authority. pub use_legacy_landlock: bool, pub windows_sandbox_level: WindowsSandboxLevel, } /// Bundled arguments for a sandbox transformation whose result will be spawned /// directly from argv. /// /// Direct-spawn callers will not run a later platform-specific launcher, so the /// returned command must encode any sandbox wrapper it needs. pub struct SandboxDirectSpawnTransformRequest<'a> { pub transform: SandboxTransformRequest<'a>, pub workspace_roots: &'a [AbsolutePathBuf], pub windows_sandbox_proxy_settings_mode: codex_windows_sandbox::WindowsSandboxProxySettingsMode, } // TODO(anp): Revisit this preparation type once this module's PathUri migration is complete. struct PendingSandboxedExecRequest { native_command_cwd: AbsolutePathBuf, native_sandbox_policy_cwd: AbsolutePathBuf, effective_permission_profile: PermissionProfile, } impl PendingSandboxedExecRequest { fn new( command_cwd: &PathUri, sandbox_policy_cwd: &PathUri, effective_permission_profile: PermissionProfile, managed_mitm_ca_trust_bundle_path: Option<&AbsolutePathBuf>, ) -> Result { // TODO(anp): Move PathUri conversion into the platform sandbox implementations. let native_command_cwd = command_cwd.to_abs_path().map_err(|source| { SandboxTransformError::InvalidCommandCwd { cwd: command_cwd.clone(), source, } })?; let native_sandbox_policy_cwd = sandbox_policy_cwd.to_abs_path().map_err(|source| { SandboxTransformError::InvalidSandboxPolicyCwd { cwd: sandbox_policy_cwd.clone(), source, } })?; let effective_permission_profile = with_managed_mitm_ca_readable_root( effective_permission_profile, managed_mitm_ca_trust_bundle_path, native_sandbox_policy_cwd.as_path(), ); Ok(Self { native_command_cwd, native_sandbox_policy_cwd, effective_permission_profile, }) } } #[derive(Debug)] pub enum SandboxTransformError { InvalidCommandCwd { cwd: PathUri, source: io::Error, }, InvalidSandboxPolicyCwd { cwd: PathUri, source: io::Error, }, MissingLinuxSandboxExecutable, WindowsMxcPreparation(String), EnvironmentNetworkProxy(String), #[cfg(target_os = "macos")] SeatbeltPreparation(String), #[cfg(target_os = "linux")] Wsl1UnsupportedForBubblewrap, #[cfg(not(target_os = "macos"))] SeatbeltUnavailable, #[cfg(target_os = "windows")] WindowsSandboxPreparation(String), } impl std::fmt::Display for SandboxTransformError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::InvalidCommandCwd { cwd, source } => { write!( f, "command cwd URI `{cwd}` is not valid on this host: {source}" ) } Self::InvalidSandboxPolicyCwd { cwd, source } => write!( f, "sandbox policy cwd URI `{cwd}` is not valid on this host: {source}" ), Self::MissingLinuxSandboxExecutable => { write!(f, "missing codex-linux-sandbox executable path") } Self::WindowsMxcPreparation(err) => { write!(f, "failed to prepare MXC sandbox: {err}") } Self::EnvironmentNetworkProxy(err) => { write!(f, "failed to prepare environment network proxy: {err}") } #[cfg(target_os = "macos")] Self::SeatbeltPreparation(err) => { write!(f, "failed to prepare Seatbelt sandbox: {err}") } #[cfg(target_os = "linux")] Self::Wsl1UnsupportedForBubblewrap => write!(f, "{WSL1_BWRAP_WARNING}"), #[cfg(not(target_os = "macos"))] Self::SeatbeltUnavailable => write!(f, "seatbelt sandbox is only available on macOS"), #[cfg(target_os = "windows")] Self::WindowsSandboxPreparation(err) => { write!(f, "failed to prepare windows sandbox wrapper: {err}") } } } } impl std::error::Error for SandboxTransformError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::InvalidCommandCwd { source, .. } | Self::InvalidSandboxPolicyCwd { source, .. } => Some(source), Self::MissingLinuxSandboxExecutable => None, Self::WindowsMxcPreparation(_) => None, Self::EnvironmentNetworkProxy(_) => None, #[cfg(target_os = "macos")] Self::SeatbeltPreparation(_) => None, #[cfg(target_os = "linux")] Self::Wsl1UnsupportedForBubblewrap => None, #[cfg(not(target_os = "macos"))] Self::SeatbeltUnavailable => None, #[cfg(target_os = "windows")] Self::WindowsSandboxPreparation(_) => None, } } } #[derive(Clone, Default)] pub struct SandboxManager { linux_sandbox_pid_namespace: LinuxSandboxPidNamespace, #[cfg(target_os = "macos")] seatbelt_profile: MacosSeatbeltProfile, #[cfg(target_os = "macos")] allowed_symlinked_codex_home: Option, } impl SandboxManager { pub fn new() -> Self { Self::default() } /// Creates a manager that applies the narrower runtime profile required by filesystem helpers. pub fn for_file_system_helpers() -> Self { Self { linux_sandbox_pid_namespace: LinuxSandboxPidNamespace::default(), #[cfg(target_os = "macos")] seatbelt_profile: MacosSeatbeltProfile::FileSystemHelper, #[cfg(target_os = "macos")] allowed_symlinked_codex_home: None, } } /// Applies a trusted executor startup policy, never a command or repository setting. pub fn with_linux_sandbox_pid_namespace(mut self, mode: LinuxSandboxPidNamespace) -> Self { self.linux_sandbox_pid_namespace = mode; self } /// Allows otherwise-authorized writable roots beneath the opted-in user home /// to follow symlinks, including targets outside that home. #[cfg(target_os = "macos")] pub fn with_allowed_symlinked_codex_home( mut self, allowed_symlinked_codex_home: Option, ) -> Self { self.allowed_symlinked_codex_home = allowed_symlinked_codex_home; self } pub fn select_initial( &self, permission_profile: &PermissionProfile, pref: SandboxablePreference, windows_sandbox_type: SandboxType, has_managed_network_requirements: bool, ) -> SandboxType { #[cfg(windows)] crate::windows_mxc::record_availability_once(); if !self.should_sandbox(permission_profile, pref, has_managed_network_requirements) { return SandboxType::None; } if cfg!(windows) && windows_sandbox_type == SandboxType::WindowsMxc { return SandboxType::WindowsMxc; } get_platform_sandbox(windows_sandbox_type != SandboxType::None).unwrap_or(SandboxType::None) } /// Returns whether the request needs a sandbox, independently of whether /// this host can provide a concrete sandbox implementation. pub fn should_sandbox( &self, permission_profile: &PermissionProfile, pref: SandboxablePreference, has_managed_network_requirements: bool, ) -> bool { match pref { SandboxablePreference::Forbid => false, SandboxablePreference::Require => true, SandboxablePreference::Auto => { let (file_system_policy, network_policy) = permission_profile.to_runtime_permissions(); should_require_platform_sandbox( &file_system_policy, network_policy, has_managed_network_requirements, ) } } } pub fn transform( &self, request: SandboxTransformRequest<'_>, ) -> Result { let SandboxTransformRequest { mut command, permissions, sandbox, enforce_managed_network, environment_id, network, sandbox_policy_cwd, sandbox_exe, use_legacy_landlock, windows_sandbox_level, } = request; #[cfg(target_os = "macos")] let managed_network = command.managed_network.as_ref(); let additional_permissions = command.additional_permissions.take(); let managed_mitm_ca_trust_bundle_path = network.and_then(NetworkProxy::managed_mitm_ca_trust_bundle_path); let base_effective_permission_profile = effective_permission_profile(permissions, additional_permissions.as_ref()); let pending_sandboxed_request = PendingSandboxedExecRequest::new( &command.cwd, sandbox_policy_cwd, base_effective_permission_profile.clone(), managed_mitm_ca_trust_bundle_path.as_ref(), ); let mut argv = Vec::with_capacity(1 + command.args.len()); argv.push(os_string_to_command_component(command.program)); argv.extend(command.args); let (argv, arg0_override, pending_sandboxed_request) = match sandbox { SandboxType::None => (argv, None, None), SandboxType::WindowsMxc => { if !codex_mxc_sandbox::is_available() { return Err(SandboxTransformError::WindowsMxcPreparation( "native MXC is unavailable on this executor".to_string(), )); } if enforce_managed_network && command.managed_network.is_none() { let network = network.ok_or_else(|| { SandboxTransformError::WindowsMxcPreparation( "managed networking requires an executor-local proxy".to_string(), ) })?; let prepared = network .prepare_for_optional_environment( std::mem::take(&mut command.env), environment_id, ) .map_err(|err| { SandboxTransformError::EnvironmentNetworkProxy(err.to_string()) })?; command.env = prepared.env; command.managed_network = Some(prepared.sandbox_context); } let managed_network = command.managed_network.filter(|_| enforce_managed_network); let pending = pending_sandboxed_request?; let exe = sandbox_exe.ok_or_else(|| { SandboxTransformError::WindowsMxcPreparation( "missing Codex executable path".to_string(), ) })?; let mut full_command = vec![os_string_to_command_component(exe.as_os_str().to_owned())]; full_command.extend( codex_mxc_sandbox::create_command_args( codex_mxc_sandbox::CreateMxcCommandArgsParams { command: argv, permission_profile: &pending.effective_permission_profile, sandbox_policy_cwd: pending.native_sandbox_policy_cwd.as_path(), managed_network: managed_network.as_ref(), env: &mut command.env, }, ) .map_err(|err| SandboxTransformError::WindowsMxcPreparation(err.to_string()))?, ); (full_command, None, Some(pending)) } #[cfg(target_os = "macos")] SandboxType::MacosSeatbelt => { use crate::seatbelt::CreateSeatbeltCommandArgsParams; use crate::seatbelt::MACOS_PATH_TO_SEATBELT_EXECUTABLE; use crate::seatbelt::SeatbeltPreparationError; use crate::seatbelt::create_seatbelt_command_args_with_profile; let pending = pending_sandboxed_request?; let (file_system_sandbox_policy, network_sandbox_policy) = pending .effective_permission_profile .to_runtime_permissions(); let mut args = create_seatbelt_command_args_with_profile( CreateSeatbeltCommandArgsParams { command: argv, file_system_sandbox_policy: &file_system_sandbox_policy, network_sandbox_policy, sandbox_policy_cwd: pending.native_sandbox_policy_cwd.as_path(), enforce_managed_network, managed_network, environment_id, network, extra_allow_unix_sockets: &[], }, self.seatbelt_profile, self.allowed_symlinked_codex_home.as_ref(), ) .map_err(|err| match err { SeatbeltPreparationError::FileSystem(message) => { SandboxTransformError::SeatbeltPreparation(message) } SeatbeltPreparationError::EnvironmentNetworkProxy(message) => { SandboxTransformError::EnvironmentNetworkProxy(message) } })?; let mut full_command = Vec::with_capacity(1 + args.len()); full_command.push(MACOS_PATH_TO_SEATBELT_EXECUTABLE.to_string()); full_command.append(&mut args); (full_command, None, Some(pending)) } #[cfg(not(target_os = "macos"))] SandboxType::MacosSeatbelt => return Err(SandboxTransformError::SeatbeltUnavailable), SandboxType::LinuxSeccomp => { let pending = pending_sandboxed_request?; let exe = sandbox_exe.ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?; if enforce_managed_network && command.managed_network.is_none() && let Some(network) = network { let prepared = network .prepare_for_optional_environment( std::mem::take(&mut command.env), environment_id, ) .map_err(|err| { SandboxTransformError::EnvironmentNetworkProxy(err.to_string()) })?; command.env = prepared.env; command.managed_network = Some(prepared.sandbox_context); } let managed_network = enforce_managed_network.then(|| command.managed_network.unwrap_or_default()); #[cfg(target_os = "linux")] ensure_linux_bubblewrap_is_supported( &pending .effective_permission_profile .file_system_sandbox_policy(), use_legacy_landlock, managed_network.is_some(), is_wsl1(), )?; let mut args = create_linux_sandbox_command_args_for_permission_profile( argv, pending.native_command_cwd.as_path(), &pending.effective_permission_profile, pending.native_sandbox_policy_cwd.as_path(), use_legacy_landlock, managed_network.as_ref(), ); // Keep default invocations compatible with older helpers. Only the // startup opt-in requires a helper that understands PID inheritance. if self.linux_sandbox_pid_namespace == LinuxSandboxPidNamespace::Inherit { args.insert(0, "--inherit-pid-namespace".to_string()); } let mut full_command = Vec::with_capacity(1 + args.len()); full_command.push(os_string_to_command_component(exe.as_os_str().to_owned())); full_command.append(&mut args); ( full_command, Some(linux_sandbox_arg0_override(exe)), Some(pending), ) } #[cfg(target_os = "windows")] SandboxType::WindowsRestrictedToken => { if enforce_managed_network && windows_sandbox_level != WindowsSandboxLevel::Elevated { return Err(SandboxTransformError::WindowsSandboxPreparation( "managed networking requires the elevated Windows sandbox backend" .to_string(), )); } let pending = pending_sandboxed_request?; (argv, None, Some(pending)) } #[cfg(not(target_os = "windows"))] SandboxType::WindowsRestrictedToken => (argv, None, Some(pending_sandboxed_request?)), }; // Unsandboxed exec-server requests may have foreign cwd values that cannot be prepared // locally, but their effective permissions must still be preserved. In that case, carry // forward the base profile. let permission_profile = pending_sandboxed_request .map_or(base_effective_permission_profile, |pending| { pending.effective_permission_profile }); Ok(SandboxExecRequest { command: argv, cwd: command.cwd, sandbox_policy_cwd: sandbox_policy_cwd.clone(), env: command.env, network: network.cloned(), network_environment_id: environment_id.map(str::to_string), sandbox, windows_sandbox_level, permission_profile, arg0: arg0_override, }) } pub fn transform_for_direct_spawn( &self, request: SandboxDirectSpawnTransformRequest<'_>, ) -> Result { #[cfg(target_os = "windows")] if request.transform.sandbox == SandboxType::WindowsRestrictedToken { let codex_home = codex_utils_home_dir::find_codex_home() .map_err(|err| SandboxTransformError::WindowsSandboxPreparation(err.to_string()))?; return self.transform_for_direct_spawn_with_codex_home(request, codex_home.as_path()); } self.transform(request.transform) } #[cfg(target_os = "windows")] fn transform_for_direct_spawn_with_codex_home( &self, request: SandboxDirectSpawnTransformRequest<'_>, codex_home: &Path, ) -> Result { let workspace_roots = request.workspace_roots; let proxy_settings_mode = request.windows_sandbox_proxy_settings_mode; let sandbox_exe = request.transform.sandbox_exe.ok_or_else(|| { SandboxTransformError::WindowsSandboxPreparation( "missing Codex executable path".to_string(), ) })?; let mut request = self.transform(request.transform)?; wrap_windows_sandbox_exec_request_for_direct_spawn( &mut request, workspace_roots, codex_home, sandbox_exe, proxy_settings_mode, )?; Ok(request) } } #[cfg(target_os = "windows")] fn wrap_windows_sandbox_exec_request_for_direct_spawn( request: &mut SandboxExecRequest, workspace_roots: &[AbsolutePathBuf], codex_home: &Path, sandbox_exe: &Path, proxy_settings_mode: codex_windows_sandbox::WindowsSandboxProxySettingsMode, ) -> Result<(), SandboxTransformError> { // TODO(anp): Keep PathUri through the Windows sandbox wrapper boundary. let native_cwd = request .cwd .to_abs_path() .map_err(|source| SandboxTransformError::InvalidCommandCwd { cwd: request.cwd.clone(), source, })?; let native_sandbox_policy_cwd = request.sandbox_policy_cwd.to_abs_path().map_err(|source| { SandboxTransformError::InvalidSandboxPolicyCwd { cwd: request.sandbox_policy_cwd.clone(), source, } })?; let Some(program) = request.command.first_mut() else { return Err(SandboxTransformError::WindowsSandboxPreparation( "sandbox command was empty".to_string(), )); }; // transform() may have made the inner command a Codex helper. Only that // helper needs materializing; an arbitrary workload such as cmd.exe does not. if Path::new(program.as_str()) == sandbox_exe { let helper = codex_windows_sandbox::resolve_exe_for_launch(sandbox_exe, codex_home); *program = helper.to_string_lossy().into_owned(); } let inner_command = std::mem::take(&mut request.command); let proxy_enforced = request.network.is_some(); let network_proxy_restricting_sid = request .network .as_ref() .map(|network| { network .network_proxy_restricting_sid(request.network_environment_id.as_deref()) .ok_or_else(|| { SandboxTransformError::WindowsSandboxPreparation( "managed Windows proxy route is missing its restricting SID".to_string(), ) }) }) .transpose()?; let use_elevated = windows_sandbox_uses_elevated_backend(request.windows_sandbox_level); let overrides = if use_elevated { resolve_windows_elevated_filesystem_overrides( request.sandbox, &request.permission_profile, &native_sandbox_policy_cwd, use_elevated, &request.env, ) } else { resolve_windows_restricted_token_filesystem_overrides( request.sandbox, &request.permission_profile, &native_sandbox_policy_cwd, request.windows_sandbox_level, ) } .map_err(SandboxTransformError::WindowsSandboxPreparation)?; let empty_paths: &[AbsolutePathBuf] = &[]; let read_roots_override = overrides .as_ref() .and_then(|overrides| overrides.read_roots_override.as_deref()); let read_roots_include_platform_defaults = overrides .as_ref() .is_some_and(|overrides| overrides.read_roots_include_platform_defaults); let write_roots_override = overrides .as_ref() .and_then(|overrides| overrides.write_roots_override.as_deref()); let deny_read_paths_override = overrides.as_ref().map_or(empty_paths, |overrides| { overrides.additional_deny_read_paths.as_slice() }); let deny_write_paths_override = overrides.as_ref().map_or(empty_paths, |overrides| { overrides.additional_deny_write_paths.as_slice() }); let mut wrapper_args = codex_windows_sandbox::create_windows_sandbox_command_args_for_permission_profile( inner_command, &native_cwd, workspace_roots, &mut request.env, &request.permission_profile, request.windows_sandbox_level, proxy_enforced, network_proxy_restricting_sid.as_deref(), proxy_settings_mode, read_roots_override, read_roots_include_platform_defaults, write_roots_override, deny_read_paths_override, deny_write_paths_override, codex_home, ) .map_err(|err| SandboxTransformError::WindowsSandboxPreparation(err.to_string()))?; request.command = Vec::with_capacity(1 + wrapper_args.len()); // This outer process interprets Codex wrapper arguments, so it must be the // supplied Codex executable even when the inner command is another program. request .command .push(sandbox_exe.to_string_lossy().into_owned()); request.command.append(&mut wrapper_args); request.sandbox = SandboxType::None; request.arg0 = None; add_windows_sandbox_wrapper_setup_env(&mut request.env); Ok(()) } #[cfg(target_os = "windows")] fn add_windows_sandbox_wrapper_setup_env(env: &mut HashMap) { add_windows_sandbox_wrapper_setup_env_from_vars( env, std::env::vars_os(), codex_windows_sandbox::registered_core_requested(), ); } #[cfg(target_os = "windows")] fn add_windows_sandbox_wrapper_setup_env_from_vars( env: &mut HashMap, vars: impl IntoIterator, registered_core: bool, ) { // This outer helper must use the parent's runtime selection, not shell-policy overrides. env.retain(|key, _| !key.eq_ignore_ascii_case("CODEX_WINDOWS_REGISTERED_CORE")); if registered_core { env.insert("CODEX_WINDOWS_REGISTERED_CORE".into(), "1".into()); } for (key, value) in vars { let key = key.to_string_lossy().into_owned(); if !WINDOWS_SANDBOX_WRAPPER_SETUP_ENV_ALLOWLIST .iter() .any(|allowed| key.eq_ignore_ascii_case(allowed)) { continue; } env.retain(|existing, _| !existing.eq_ignore_ascii_case(&key)); env.insert(key, value.to_string_lossy().into_owned()); } } pub fn compatibility_sandbox_policy_for_permission_profile( permissions: &PermissionProfile, cwd: &Path, ) -> SandboxPolicy { permissions .to_legacy_sandbox_policy(cwd) .unwrap_or_else(|_| { let (file_system_policy, network_policy) = permissions.to_runtime_permissions(); compatibility_workspace_write_policy(file_system_policy, network_policy, cwd) }) } fn compatibility_workspace_write_policy( file_system_policy: FileSystemSandboxPolicy, network_policy: NetworkSandboxPolicy, cwd: &Path, ) -> SandboxPolicy { let cwd_abs = AbsolutePathBuf::from_absolute_path(cwd).ok(); let writable_roots = file_system_policy .get_writable_roots_with_cwd(cwd) .into_iter() .map(|root| root.root) .filter(|root| cwd_abs.as_ref() != Some(root)) .collect(); let tmpdir_writable = std::env::var_os("TMPDIR") .filter(|tmpdir| !tmpdir.is_empty()) .and_then(|tmpdir| { AbsolutePathBuf::from_absolute_path(std::path::PathBuf::from(tmpdir)).ok() }) .is_some_and(|tmpdir| { file_system_policy.can_write_local_path_with_cwd(tmpdir.as_path(), cwd) }); let slash_tmp = Path::new("/tmp"); let slash_tmp_writable = slash_tmp.is_absolute() && slash_tmp.is_dir() && file_system_policy.can_write_local_path_with_cwd(slash_tmp, cwd); SandboxPolicy::WorkspaceWrite { writable_roots, network_access: network_policy.is_enabled(), exclude_tmpdir_env_var: !tmpdir_writable, exclude_slash_tmp: !slash_tmp_writable, } } #[cfg(target_os = "linux")] fn ensure_linux_bubblewrap_is_supported( file_system_sandbox_policy: &FileSystemSandboxPolicy, use_legacy_landlock: bool, allow_network_for_proxy: bool, is_wsl1: bool, ) -> Result<(), SandboxTransformError> { let requires_bubblewrap = allow_network_for_proxy || (!use_legacy_landlock && !file_system_sandbox_policy.has_full_disk_write_access()); if is_wsl1 && requires_bubblewrap { return Err(SandboxTransformError::Wsl1UnsupportedForBubblewrap); } Ok(()) } fn os_string_to_command_component(value: OsString) -> String { value .into_string() .unwrap_or_else(|value| value.to_string_lossy().into_owned()) } fn linux_sandbox_arg0_override(exe: &Path) -> String { if exe.file_name().and_then(|name| name.to_str()) == Some(CODEX_LINUX_SANDBOX_ARG0) { os_string_to_command_component(exe.as_os_str().to_owned()) } else { CODEX_LINUX_SANDBOX_ARG0.to_string() } } #[cfg(test)] #[path = "manager_tests.rs"] mod tests;