diff --git a/codex-rs/core/src/sandboxing/mac/mod.rs b/codex-rs/core/src/sandboxing/mac/mod.rs index c625d0b047..c51ad32ae5 100644 --- a/codex-rs/core/src/sandboxing/mac/mod.rs +++ b/codex-rs/core/src/sandboxing/mac/mod.rs @@ -1 +1,4 @@ pub mod seatbelt; + +#[cfg(target_os = "macos")] +pub mod sys; diff --git a/codex-rs/core/src/sandboxing/mac/seatbelt.rs b/codex-rs/core/src/sandboxing/mac/seatbelt.rs index 0c7ad7fb14..055d686d38 100644 --- a/codex-rs/core/src/sandboxing/mac/seatbelt.rs +++ b/codex-rs/core/src/sandboxing/mac/seatbelt.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; -use std::ffi::CStr; use std::path::Path; use std::path::PathBuf; use tokio::process::Child; use crate::protocol::SandboxPolicy; +#[cfg(target_os = "macos")] +use crate::sandboxing::mac::sys; use crate::spawn::CODEX_SANDBOX_ENV_VAR; use crate::spawn::StdioPolicy; use crate::spawn::spawn_child_async; @@ -102,18 +103,26 @@ pub(crate) fn create_seatbelt_command_args( "" }; - // TODO(mbolin): apply_patch calls must also honor the SandboxPolicy. let network_policy = if sandbox_policy.has_full_network_access() { MACOS_SEATBELT_NETWORK_POLICY } else { "" }; + let (user_cache_dir_policy, user_cache_dir_params) = user_cache_dir() + .map(|p| { + ( + "(allow file-write* (subpath (param \"DARWIN_USER_CACHE_DIR\")))", + vec![("DARWIN_USER_CACHE_DIR".to_string(), p)], + ) + }) + .unwrap_or_default(); + let full_policy = format!( - "{MACOS_SEATBELT_BASE_POLICY}\n{file_read_policy}\n{file_write_policy}\n{network_policy}" + "{MACOS_SEATBELT_BASE_POLICY}\n{file_read_policy}\n{file_write_policy}\n{network_policy}\n{user_cache_dir_policy}" ); - let dir_params = [file_write_dir_params, macos_dir_params()].concat(); + let dir_params = [file_write_dir_params, user_cache_dir_params].concat(); let mut seatbelt_args: Vec = vec!["-p".to_string(), full_policy]; let definition_args = dir_params @@ -125,39 +134,22 @@ pub(crate) fn create_seatbelt_command_args( seatbelt_args } -/// Wraps libc::confstr to return a String. -fn confstr(name: libc::c_int) -> Option { - let mut buf = vec![0_i8; (libc::PATH_MAX as usize) + 1]; - let len = unsafe { libc::confstr(name, buf.as_mut_ptr(), buf.len()) }; - if len == 0 { - return None; - } - // confstr guarantees NUL-termination when len > 0. - let cstr = unsafe { CStr::from_ptr(buf.as_ptr()) }; - cstr.to_str().ok().map(ToString::to_string) +#[cfg(target_os = "macos")] +fn user_cache_dir() -> Option { + sys::user_cache_dir() } -/// Wraps confstr to return a canonicalized PathBuf. -fn confstr_path(name: libc::c_int) -> Option { - let s = confstr(name)?; - let path = PathBuf::from(s); - path.canonicalize().ok().or(Some(path)) -} - -fn macos_dir_params() -> Vec<(String, PathBuf)> { - #[cfg(target_os = "macos")] - if let Some(p) = confstr_path(libc::_CS_DARWIN_USER_CACHE_DIR) { - return vec![("DARWIN_USER_CACHE_DIR".to_string(), p)]; - } - vec![] +#[cfg(not(target_os = "macos"))] +fn user_cache_dir() -> Option { + None } #[cfg(test)] mod tests { use super::MACOS_SEATBELT_BASE_POLICY; use super::create_seatbelt_command_args; - use super::macos_dir_params; use crate::protocol::SandboxPolicy; + use crate::seatbelt::user_cache_dir; use pretty_assertions::assert_eq; use std::fs; use std::path::Path; @@ -226,11 +218,9 @@ mod tests { format!("-DWRITABLE_ROOT_2={}", cwd.to_string_lossy()), ]; - expected_args.extend( - macos_dir_params() - .into_iter() - .map(|(key, value)| format!("-D{key}={value}", value = value.to_string_lossy())), - ); + if let Some(p) = user_cache_dir() { + expected_args.push(format!("-DDARWIN_USER_CACHE_DIR={}", p.to_string_lossy())); + } expected_args.extend(vec![ "--".to_string(), @@ -320,11 +310,9 @@ mod tests { expected_args.push(format!("-DWRITABLE_ROOT_2={p}")); } - expected_args.extend( - macos_dir_params() - .into_iter() - .map(|(key, value)| format!("-D{key}={value}", value = value.to_string_lossy())), - ); + if let Some(p) = user_cache_dir() { + expected_args.push(format!("-DDARWIN_USER_CACHE_DIR={}", p.to_string_lossy())); + } expected_args.extend(vec![ "--".to_string(), diff --git a/codex-rs/core/src/sandboxing/mac/sys.rs b/codex-rs/core/src/sandboxing/mac/sys.rs new file mode 100644 index 0000000000..127bf6c920 --- /dev/null +++ b/codex-rs/core/src/sandboxing/mac/sys.rs @@ -0,0 +1,28 @@ +use std::ffi::CStr; +use std::path::PathBuf; +use std::string::ToString; + +use libc; + +/// Wraps libc::confstr to return a String. +fn confstr(name: libc::c_int) -> Option { + let mut buf = vec![0_i8; (libc::PATH_MAX as usize) + 1]; + let len = unsafe { libc::confstr(name, buf.as_mut_ptr(), buf.len()) }; + if len == 0 { + return None; + } + // confstr guarantees NUL-termination when len > 0. + let cstr = unsafe { CStr::from_ptr(buf.as_ptr()) }; + cstr.to_str().ok().map(ToString::to_string) +} + +/// Wraps confstr to return a canonicalized PathBuf. +fn confstr_path(name: libc::c_int) -> Option { + let s = confstr(name)?; + let path = PathBuf::from(s); + path.canonicalize().ok().or(Some(path)) +} + +pub fn user_cache_dir() -> Option { + confstr_path(libc::_CS_DARWIN_USER_CACHE_DIR) +} diff --git a/codex-rs/core/src/seatbelt_network_policy.sbpl b/codex-rs/core/src/seatbelt_network_policy.sbpl index 2a72f95fd3..046879bb35 100644 --- a/codex-rs/core/src/seatbelt_network_policy.sbpl +++ b/codex-rs/core/src/seatbelt_network_policy.sbpl @@ -24,7 +24,3 @@ (allow sysctl-read (sysctl-name-regex #"^net.routetable") ) - -(allow file-write* - (subpath (param "DARWIN_USER_CACHE_DIR")) -)