Fix macOS fs helper startup runtime policy

This commit is contained in:
Javier Soto
2026-05-28 12:33:47 -07:00
parent ecb41fcb64
commit 2628c0becf
8 changed files with 185 additions and 70 deletions

View File

@@ -154,16 +154,15 @@ fn add_helper_runtime_permissions(
helper_read_roots: &[AbsolutePathBuf],
cwd: &std::path::Path,
) {
if !file_system_policy.has_full_disk_read_access() {
let minimal_read_entry = FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
};
if !file_system_policy.entries.contains(&minimal_read_entry) {
file_system_policy.entries.push(minimal_read_entry);
}
// `:minimal` includes native runtime permissions needed before the helper reaches Rust.
let minimal_read_entry = FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
};
if !file_system_policy.entries.contains(&minimal_read_entry) {
file_system_policy.entries.push(minimal_read_entry);
}
for helper_read_root in helper_read_roots {
@@ -361,6 +360,24 @@ mod tests {
assert!(policy.include_platform_defaults());
}
#[test]
fn helper_permissions_enable_minimal_runtime_for_full_disk_read_profile() {
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path())
.expect("absolute cwd");
let minimal_read_entry =
special_entry(FileSystemSpecialPath::Minimal, FileSystemAccessMode::Read);
let mut policy = restricted_policy(vec![special_entry(
FileSystemSpecialPath::Root,
FileSystemAccessMode::Read,
)]);
add_helper_runtime_permissions(&mut policy, /*helper_read_roots*/ &[], cwd.as_path());
assert!(policy.entries.contains(&minimal_read_entry));
assert!(policy.include_platform_runtime_defaults());
assert!(!policy.include_platform_defaults());
}
#[test]
fn helper_permissions_preserve_existing_writes() {
let codex_self_exe = std::env::current_exe().expect("current exe");

View File

@@ -213,6 +213,7 @@ struct ResolvedFileSystemEntry {
struct FileSystemSemanticSignature {
has_full_disk_read_access: bool,
has_full_disk_write_access: bool,
include_platform_runtime_defaults: bool,
include_platform_defaults: bool,
readable_roots: Vec<AbsolutePathBuf>,
writable_roots: Vec<WritableRoot>,
@@ -633,10 +634,9 @@ impl FileSystemSandboxPolicy {
}
}
/// Returns true when platform-default readable roots should be included.
pub fn include_platform_defaults(&self) -> bool {
!self.has_full_disk_read_access()
&& matches!(self.kind, FileSystemSandboxKind::Restricted)
/// Returns true when minimum native runtime permissions should be included.
pub fn include_platform_runtime_defaults(&self) -> bool {
matches!(self.kind, FileSystemSandboxKind::Restricted)
&& self.entries.iter().any(|entry| {
matches!(
&entry.path,
@@ -647,6 +647,11 @@ impl FileSystemSandboxPolicy {
})
}
/// Returns true when platform-default readable roots should be included.
pub fn include_platform_defaults(&self) -> bool {
!self.has_full_disk_read_access() && self.include_platform_runtime_defaults()
}
pub fn resolve_access_with_cwd(&self, path: &Path, cwd: &Path) -> FileSystemAccessMode {
match self.kind {
FileSystemSandboxKind::Unrestricted | FileSystemSandboxKind::ExternalSandbox => {
@@ -1233,6 +1238,7 @@ impl FileSystemSandboxPolicy {
FileSystemSemanticSignature {
has_full_disk_read_access: self.has_full_disk_read_access(),
has_full_disk_write_access: self.has_full_disk_write_access(),
include_platform_runtime_defaults: self.include_platform_runtime_defaults(),
include_platform_defaults: self.include_platform_defaults(),
readable_roots: sorted_absolute_paths(self.get_readable_roots_with_cwd(cwd)),
writable_roots: sorted_writable_roots(self.get_writable_roots_with_cwd(cwd)),

View File

@@ -4218,8 +4218,31 @@ mod tests {
}]);
assert!(read_only.has_full_disk_read_access());
assert!(!read_only.has_full_disk_write_access());
assert!(!read_only.include_platform_runtime_defaults());
assert!(!read_only.include_platform_defaults());
let read_only_with_minimal_runtime = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
},
]);
assert!(read_only_with_minimal_runtime.has_full_disk_read_access());
assert!(read_only_with_minimal_runtime.include_platform_runtime_defaults());
assert!(!read_only_with_minimal_runtime.include_platform_defaults());
assert!(!read_only.is_semantically_equivalent_to(
&read_only_with_minimal_runtime,
std::path::Path::new("/")
));
let writable = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,

View File

@@ -4,6 +4,7 @@ codex_rust_crate(
name = "sandboxing",
crate_name = "codex_sandboxing",
compile_data = [
"src/restricted_platform_runtime_defaults.sbpl",
"src/restricted_read_only_platform_defaults.sbpl",
"src/seatbelt_base_policy.sbpl",
"src/seatbelt_network_policy.sbpl",

View File

@@ -0,0 +1,56 @@
; macOS non-file runtime defaults included when a split filesystem policy requests `:minimal`.
; 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 FSIOC_CAS_BSDFLAGS as alternate chflags.
(allow system-fsctl (fsctl-command FSIOC_CAS_BSDFLAGS))
; 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"))
; 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"))

View File

@@ -1,4 +1,5 @@
; macOS platform defaults included when a split filesystem policy requests `:minimal`.
; macOS platform file defaults included when a split filesystem policy requests `:minimal`
; without already granting full disk read access.
; Read access to standard system paths
(allow file-read* file-test-existence
@@ -40,15 +41,6 @@
(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")
@@ -64,9 +56,6 @@
(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")
@@ -109,53 +98,10 @@
(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"))

View File

@@ -19,6 +19,8 @@ use url::Url;
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_RESTRICTED_PLATFORM_RUNTIME_DEFAULTS: &str =
include_str!("restricted_platform_runtime_defaults.sbpl");
const MACOS_RESTRICTED_READ_ONLY_PLATFORM_DEFAULTS: &str =
include_str!("restricted_read_only_platform_defaults.sbpl");
@@ -705,6 +707,8 @@ pub fn create_seatbelt_command_args(args: CreateSeatbeltCommandArgsParams<'_>) -
let network_policy =
dynamic_network_policy_for_network(network_sandbox_policy, enforce_managed_network, &proxy);
let include_platform_runtime_defaults =
file_system_sandbox_policy.include_platform_runtime_defaults();
let include_platform_defaults = file_system_sandbox_policy.include_platform_defaults();
let deny_read_policy =
build_seatbelt_unreadable_glob_policy(file_system_sandbox_policy, sandbox_policy_cwd);
@@ -715,6 +719,9 @@ pub fn create_seatbelt_command_args(args: CreateSeatbeltCommandArgsParams<'_>) -
deny_read_policy,
network_policy,
];
if include_platform_runtime_defaults {
policy_sections.push(MACOS_RESTRICTED_PLATFORM_RUNTIME_DEFAULTS.to_string());
}
if include_platform_defaults {
policy_sections.push(MACOS_RESTRICTED_READ_ONLY_PLATFORM_DEFAULTS.to_string());
}

View File

@@ -108,6 +108,65 @@ fn base_policy_allows_node_cpu_sysctls() {
);
}
#[test]
fn minimal_runtime_allows_core_foundation_user_lookup_ipc_without_expanding_base_policy() {
assert!(
!MACOS_SEATBELT_BASE_POLICY.contains("(global-name \"com.apple.logd\")"),
"CoreFoundation startup logging should be scoped to minimal runtime policies:\n{MACOS_SEATBELT_BASE_POLICY}"
);
assert!(
!MACOS_SEATBELT_BASE_POLICY
.contains("(global-name \"com.apple.system.notification_center\")"),
"CoreFoundation user lookup notifications should be scoped to minimal runtime policies:\n{MACOS_SEATBELT_BASE_POLICY}"
);
assert!(
!MACOS_SEATBELT_BASE_POLICY.contains("(ipc-posix-name \"apple.shm.notification_center\")"),
"CoreFoundation notification shared memory should be scoped to minimal runtime policies:\n{MACOS_SEATBELT_BASE_POLICY}"
);
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::Minimal,
},
access: FileSystemAccessMode::Read,
},
]);
let args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
command: vec!["/bin/true".to_string()],
file_system_sandbox_policy: &file_system_policy,
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
sandbox_policy_cwd: Path::new("/"),
enforce_managed_network: false,
network: None,
extra_allow_unix_sockets: &[],
});
let policy = seatbelt_policy_arg(&args);
assert!(
policy.contains("(global-name \"com.apple.logd\")"),
"minimal runtime must allow CoreFoundation startup logging:\n{policy}"
);
assert!(
policy.contains("(global-name \"com.apple.system.notification_center\")"),
"minimal runtime must allow CoreFoundation user lookup notifications:\n{policy}"
);
assert!(
policy.contains("(ipc-posix-name \"apple.shm.notification_center\")"),
"minimal runtime must allow CoreFoundation notification shared memory:\n{policy}"
);
assert!(
!policy.contains("(subpath \"/private/tmp\")"),
"full disk read must not acquire minimal runtime temporary writes:\n{policy}"
);
}
#[test]
fn base_policy_allows_kmp_registration_shm_read_create_and_unlink() {
let expected = r##"(allow ipc-posix-shm-read-data