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:
Leo Shimonaka
2026-02-17 23:56:35 -08:00
committed by GitHub
parent f600453699
commit 1946a4c48b
3 changed files with 217 additions and 46 deletions

View File

@@ -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 = [

View 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"))

View File

@@ -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.
///