mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
fix: Restricted Read: /System is too permissive for macOS platform de… (#11798)
…fault Update the list of platform defaults included for `ReadOnlyAccess`. When `ReadOnlyAccess::Restricted::include_platform_defaults` is `true`, the policy defined in `codex-rs/core/src/seatbelt_platform_defaults.sbpl` is appended to enable macOS programs to function properly.
This commit is contained in:
@@ -23,6 +23,7 @@ use crate::spawn::spawn_child_async;
|
||||
|
||||
const MACOS_SEATBELT_BASE_POLICY: &str = include_str!("seatbelt_base_policy.sbpl");
|
||||
const MACOS_SEATBELT_NETWORK_POLICY: &str = include_str!("seatbelt_network_policy.sbpl");
|
||||
const MACOS_SEATBELT_PLATFORM_DEFAULTS: &str = include_str!("seatbelt_platform_defaults.sbpl");
|
||||
|
||||
/// When working with `sandbox-exec`, only consider `sandbox-exec` in `/usr/bin`
|
||||
/// to defend against an attacker trying to inject a malicious version on the
|
||||
@@ -314,18 +315,23 @@ pub(crate) fn create_seatbelt_command_args_with_extensions(
|
||||
build_seatbelt_extensions,
|
||||
);
|
||||
|
||||
let include_platform_defaults = sandbox_policy.include_platform_defaults();
|
||||
let mut policy_sections = vec![
|
||||
MACOS_SEATBELT_BASE_POLICY.to_string(),
|
||||
file_read_policy,
|
||||
file_write_policy,
|
||||
network_policy,
|
||||
];
|
||||
if include_platform_defaults {
|
||||
policy_sections.push(MACOS_SEATBELT_PLATFORM_DEFAULTS.to_string());
|
||||
}
|
||||
if !unix_socket_policy.is_empty() {
|
||||
policy_sections.push(unix_socket_policy);
|
||||
}
|
||||
if !seatbelt_extensions.policy.is_empty() {
|
||||
policy_sections.push(seatbelt_extensions.policy.clone());
|
||||
}
|
||||
|
||||
let full_policy = policy_sections.join("\n");
|
||||
|
||||
let dir_params = [
|
||||
|
||||
181
codex-rs/core/src/seatbelt_platform_defaults.sbpl
Normal file
181
codex-rs/core/src/seatbelt_platform_defaults.sbpl
Normal file
@@ -0,0 +1,181 @@
|
||||
; macOS platform defaults included via `ReadOnlyAccess::Restricted::include_platform_defaults`
|
||||
|
||||
; Read access to standard system paths
|
||||
(allow file-read* file-test-existence
|
||||
(subpath "/Library/Apple")
|
||||
(subpath "/Library/Filesystems/NetFSPlugins")
|
||||
(subpath "/Library/Preferences/Logging")
|
||||
(subpath "/private/var/db/DarwinDirectory/local/recordStore.data")
|
||||
(subpath "/private/var/db/timezone")
|
||||
(subpath "/usr/lib")
|
||||
(subpath "/usr/share")
|
||||
(subpath "/Library/Preferences")
|
||||
(subpath "/var/db")
|
||||
(subpath "/private/var/db"))
|
||||
|
||||
; Map system frameworks + dylibs for loader.
|
||||
(allow file-map-executable
|
||||
(subpath "/Library/Apple/System/Library/Frameworks")
|
||||
(subpath "/Library/Apple/System/Library/PrivateFrameworks")
|
||||
(subpath "/Library/Apple/usr/lib")
|
||||
(subpath "/System/Library/Extensions")
|
||||
(subpath "/System/Library/Frameworks")
|
||||
(subpath "/System/Library/PrivateFrameworks")
|
||||
(subpath "/System/Library/SubFrameworks")
|
||||
(subpath "/System/iOSSupport/System/Library/Frameworks")
|
||||
(subpath "/System/iOSSupport/System/Library/PrivateFrameworks")
|
||||
(subpath "/System/iOSSupport/System/Library/SubFrameworks")
|
||||
(subpath "/usr/lib"))
|
||||
|
||||
; Allow guarded vnodes.
|
||||
(allow system-mac-syscall (mac-policy-name "vnguard"))
|
||||
|
||||
; Determine whether a container is expected.
|
||||
(allow system-mac-syscall
|
||||
(require-all
|
||||
(mac-policy-name "Sandbox")
|
||||
(mac-syscall-number 67)))
|
||||
|
||||
; Allow resolution of standard system symlinks.
|
||||
(allow file-read-metadata file-test-existence
|
||||
(literal "/etc")
|
||||
(literal "/tmp")
|
||||
(literal "/var")
|
||||
(literal "/private/etc/localtime"))
|
||||
|
||||
; Allow stat'ing of firmlink parent path components.
|
||||
(allow file-read-metadata file-test-existence
|
||||
(path-ancestors "/System/Volumes/Data/private"))
|
||||
|
||||
; Allow processes to get their current working directory.
|
||||
(allow file-read* file-test-existence
|
||||
(literal "/"))
|
||||
|
||||
; Allow FSIOC_CAS_BSDFLAGS as alternate chflags.
|
||||
(allow system-fsctl (fsctl-command FSIOC_CAS_BSDFLAGS))
|
||||
|
||||
; Allow access to standard special files.
|
||||
(allow file-read* file-test-existence
|
||||
(literal "/dev/autofs_nowait")
|
||||
(literal "/dev/random")
|
||||
(literal "/dev/urandom")
|
||||
(literal "/private/etc/master.passwd")
|
||||
(literal "/private/etc/passwd")
|
||||
(literal "/private/etc/protocols")
|
||||
(literal "/private/etc/services"))
|
||||
|
||||
; Allow null/zero read/write.
|
||||
(allow file-read* file-test-existence file-write-data
|
||||
(literal "/dev/null")
|
||||
(literal "/dev/zero"))
|
||||
|
||||
; Allow read/write access to the file descriptors.
|
||||
(allow file-read-data file-test-existence file-write-data
|
||||
(subpath "/dev/fd"))
|
||||
|
||||
; Provide access to debugger helpers.
|
||||
(allow file-read* file-test-existence file-write-data file-ioctl
|
||||
(literal "/dev/dtracehelper"))
|
||||
|
||||
; Scratch space so tools can create temp files.
|
||||
(allow file-read* file-test-existence file-write* (subpath "/tmp"))
|
||||
(allow file-read* file-write* (subpath "/private/tmp"))
|
||||
(allow file-read* file-write* (subpath "/var/tmp"))
|
||||
(allow file-read* file-write* (subpath "/private/var/tmp"))
|
||||
|
||||
; Allow reading standard config directories.
|
||||
(allow file-read* (subpath "/etc"))
|
||||
(allow file-read* (subpath "/private/etc"))
|
||||
|
||||
; Some processes read /var metadata during startup.
|
||||
(allow file-read-metadata (subpath "/var"))
|
||||
(allow file-read-metadata (subpath "/private/var"))
|
||||
|
||||
; IOKit access for root domain services.
|
||||
(allow iokit-open
|
||||
(iokit-registry-entry-class "RootDomainUserClient"))
|
||||
|
||||
; macOS Standard library queries opendirectoryd at startup
|
||||
(allow mach-lookup (global-name "com.apple.system.opendirectoryd.libinfo"))
|
||||
|
||||
; Allow IPC to analytics, logging, trust, and other system agents.
|
||||
(allow mach-lookup
|
||||
(global-name "com.apple.analyticsd")
|
||||
(global-name "com.apple.analyticsd.messagetracer")
|
||||
(global-name "com.apple.appsleep")
|
||||
(global-name "com.apple.bsd.dirhelper")
|
||||
(global-name "com.apple.cfprefsd.agent")
|
||||
(global-name "com.apple.cfprefsd.daemon")
|
||||
(global-name "com.apple.diagnosticd")
|
||||
(global-name "com.apple.dt.automationmode.reader")
|
||||
(global-name "com.apple.espd")
|
||||
(global-name "com.apple.logd")
|
||||
(global-name "com.apple.logd.events")
|
||||
(global-name "com.apple.runningboard")
|
||||
(global-name "com.apple.secinitd")
|
||||
(global-name "com.apple.system.DirectoryService.libinfo_v1")
|
||||
(global-name "com.apple.system.logger")
|
||||
(global-name "com.apple.system.notification_center")
|
||||
(global-name "com.apple.system.opendirectoryd.membership")
|
||||
(global-name "com.apple.trustd")
|
||||
(global-name "com.apple.trustd.agent")
|
||||
(global-name "com.apple.xpc.activity.unmanaged")
|
||||
(local-name "com.apple.cfprefsd.agent"))
|
||||
|
||||
; Allow IPC to the syslog socket for logging.
|
||||
(allow network-outbound (literal "/private/var/run/syslog"))
|
||||
|
||||
; macOS Notifications
|
||||
(allow ipc-posix-shm-read*
|
||||
(ipc-posix-name "apple.shm.notification_center"))
|
||||
|
||||
; Regulatory domain support.
|
||||
(allow file-read*
|
||||
(literal "/private/var/db/eligibilityd/eligibility.plist"))
|
||||
|
||||
; Audio and power management services.
|
||||
(allow mach-lookup (global-name "com.apple.audio.audiohald"))
|
||||
(allow mach-lookup (global-name "com.apple.audio.AudioComponentRegistrar"))
|
||||
(allow mach-lookup (global-name "com.apple.PowerManagement.control"))
|
||||
|
||||
; Allow reading the minimum system runtime so exec works.
|
||||
(allow file-read-data (subpath "/bin"))
|
||||
(allow file-read-metadata (subpath "/bin"))
|
||||
(allow file-read-data (subpath "/sbin"))
|
||||
(allow file-read-metadata (subpath "/sbin"))
|
||||
(allow file-read-data (subpath "/usr/bin"))
|
||||
(allow file-read-metadata (subpath "/usr/bin"))
|
||||
(allow file-read-data (subpath "/usr/sbin"))
|
||||
(allow file-read-metadata (subpath "/usr/sbin"))
|
||||
(allow file-read-data (subpath "/usr/libexec"))
|
||||
(allow file-read-metadata (subpath "/usr/libexec"))
|
||||
|
||||
(allow file-read* (subpath "/Library/Preferences"))
|
||||
(allow file-read* (subpath "/opt/homebrew/lib"))
|
||||
(allow file-read* (subpath "/usr/local/lib"))
|
||||
(allow file-read* (subpath "/Applications"))
|
||||
|
||||
; Terminal basics and device handles.
|
||||
(allow file-read* (regex "^/dev/fd/(0|1|2)$"))
|
||||
(allow file-write* (regex "^/dev/fd/(1|2)$"))
|
||||
(allow file-read* file-write* (literal "/dev/null"))
|
||||
(allow file-read* file-write* (literal "/dev/tty"))
|
||||
(allow file-read-metadata (literal "/dev"))
|
||||
(allow file-read-metadata (regex "^/dev/.*$"))
|
||||
(allow file-read-metadata (literal "/dev/stdin"))
|
||||
(allow file-read-metadata (literal "/dev/stdout"))
|
||||
(allow file-read-metadata (literal "/dev/stderr"))
|
||||
(allow file-read-metadata (regex "^/dev/tty[^/]*$"))
|
||||
(allow file-read-metadata (regex "^/dev/pty[^/]*$"))
|
||||
(allow file-read* file-write* (regex "^/dev/ttys[0-9]+$"))
|
||||
(allow file-read* file-write* (literal "/dev/ptmx"))
|
||||
(allow file-ioctl (regex "^/dev/ttys[0-9]+$"))
|
||||
|
||||
; Allow metadata traversal for firmlink parents.
|
||||
(allow file-read-metadata (literal "/System/Volumes") (vnode-type DIRECTORY))
|
||||
(allow file-read-metadata (literal "/System/Volumes/Data") (vnode-type DIRECTORY))
|
||||
(allow file-read-metadata (literal "/System/Volumes/Data/Users") (vnode-type DIRECTORY))
|
||||
|
||||
; App sandbox extensions
|
||||
(allow file-read* (extension "com.apple.app-sandbox.read"))
|
||||
(allow file-read* file-write* (extension "com.apple.app-sandbox.read-write"))
|
||||
@@ -435,6 +435,17 @@ impl ReadOnlyAccess {
|
||||
matches!(self, ReadOnlyAccess::FullAccess)
|
||||
}
|
||||
|
||||
/// Returns true if platform defaults should be included for restricted read access.
|
||||
pub fn include_platform_defaults(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ReadOnlyAccess::Restricted {
|
||||
include_platform_defaults: true,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the readable roots for restricted read access.
|
||||
///
|
||||
/// For [`ReadOnlyAccess::FullAccess`], returns an empty list because
|
||||
@@ -442,53 +453,12 @@ impl ReadOnlyAccess {
|
||||
pub fn get_readable_roots_with_cwd(&self, cwd: &Path) -> Vec<AbsolutePathBuf> {
|
||||
let mut roots: Vec<AbsolutePathBuf> = match self {
|
||||
ReadOnlyAccess::FullAccess => return Vec::new(),
|
||||
ReadOnlyAccess::Restricted {
|
||||
include_platform_defaults,
|
||||
readable_roots,
|
||||
} => {
|
||||
ReadOnlyAccess::Restricted { readable_roots, .. } => {
|
||||
let mut roots = readable_roots.clone();
|
||||
if *include_platform_defaults {
|
||||
#[cfg(target_os = "macos")]
|
||||
for platform_path in [
|
||||
"/bin", "/dev", "/etc", "/Library", "/private", "/sbin", "/System", "/tmp",
|
||||
"/usr",
|
||||
] {
|
||||
#[allow(clippy::expect_used)]
|
||||
roots.push(
|
||||
AbsolutePathBuf::from_absolute_path(platform_path)
|
||||
.expect("platform defaults should be absolute"),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
for platform_path in ["/bin", "/dev", "/etc", "/lib", "/lib64", "/tmp", "/usr"]
|
||||
{
|
||||
#[allow(clippy::expect_used)]
|
||||
roots.push(
|
||||
AbsolutePathBuf::from_absolute_path(platform_path)
|
||||
.expect("platform defaults should be absolute"),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
for platform_path in [
|
||||
r"C:\Windows",
|
||||
r"C:\Program Files",
|
||||
r"C:\Program Files (x86)",
|
||||
r"C:\ProgramData",
|
||||
] {
|
||||
#[allow(clippy::expect_used)]
|
||||
roots.push(
|
||||
AbsolutePathBuf::from_absolute_path(platform_path)
|
||||
.expect("platform defaults should be absolute"),
|
||||
);
|
||||
}
|
||||
|
||||
match AbsolutePathBuf::from_absolute_path(cwd) {
|
||||
Ok(cwd_root) => roots.push(cwd_root),
|
||||
Err(err) => {
|
||||
error!("Ignoring invalid cwd {cwd:?} for sandbox readable root: {err}");
|
||||
}
|
||||
match AbsolutePathBuf::from_absolute_path(cwd) {
|
||||
Ok(cwd_root) => roots.push(cwd_root),
|
||||
Err(err) => {
|
||||
error!("Ignoring invalid cwd {cwd:?} for sandbox readable root: {err}");
|
||||
}
|
||||
}
|
||||
roots
|
||||
@@ -653,6 +623,20 @@ impl SandboxPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if platform defaults should be included for restricted read access.
|
||||
pub fn include_platform_defaults(&self) -> bool {
|
||||
if self.has_full_disk_read_access() {
|
||||
return false;
|
||||
}
|
||||
match self {
|
||||
SandboxPolicy::ReadOnly { access } => access.include_platform_defaults(),
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
read_only_access, ..
|
||||
} => read_only_access.include_platform_defaults(),
|
||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of readable roots (tailored to the current working
|
||||
/// directory) when read access is restricted.
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user