mirror of
https://github.com/openai/codex.git
synced 2026-05-29 23:40:29 +00:00
Fix macOS fs helper startup runtime policy
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"))
|
||||
@@ -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"))
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user