From 4ae60cf03cf5ea4fbea33bed4b047b9ce8e59a0b Mon Sep 17 00:00:00 2001 From: Leo Shimonaka Date: Sun, 1 Mar 2026 11:00:54 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20MacOSAutomationPermission::BundleIDs=20s?= =?UTF-8?q?hould=20allow=20communicating=20=E2=80=A6=20(#12989)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …with launchservicesd Add mach lookup for `launchservicesd` when extending the sandbox for `MacOSAutomationPermission::BundleIDs`. This is necessary so that the target application can be launched for automation. This omission was due to a spec error in a document, which has been fixed. --- codex-rs/core/src/seatbelt.rs | 51 +++++++++++++++++++++++ codex-rs/core/src/seatbelt_permissions.rs | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/codex-rs/core/src/seatbelt.rs b/codex-rs/core/src/seatbelt.rs index d3f630738..c9c280120 100644 --- a/codex-rs/core/src/seatbelt.rs +++ b/codex-rs/core/src/seatbelt.rs @@ -581,6 +581,57 @@ mod tests { assert!(policy.contains("com.apple.CalendarAgent")); } + #[test] + fn bundle_id_automation_keeps_lsopen_denied() { + let tmp = TempDir::new().expect("tempdir"); + let cwd = tmp.path().join("cwd"); + fs::create_dir_all(&cwd).expect("create cwd"); + + let args = create_seatbelt_command_args_with_extensions( + vec![ + "/usr/bin/python3".to_string(), + "-c".to_string(), + r#"import ctypes +import os +import sys +lib = ctypes.CDLL("/usr/lib/libsandbox.1.dylib") +lib.sandbox_check.restype = ctypes.c_int +allowed = lib.sandbox_check(os.getpid(), b"lsopen", 0) == 0 +sys.exit(0 if allowed else 13) +"# + .to_string(), + ], + &SandboxPolicy::new_read_only_policy(), + cwd.as_path(), + false, + None, + Some(&MacOsSeatbeltProfileExtensions { + macos_automation: MacOsAutomationPermission::BundleIds(vec![ + "com.apple.Notes".to_string(), + ]), + ..Default::default() + }), + ); + + let output = Command::new(MACOS_PATH_TO_SEATBELT_EXECUTABLE) + .args(&args) + .current_dir(&cwd) + .output() + .expect("execute seatbelt command"); + + let stderr = String::from_utf8_lossy(&output.stderr); + if stderr.contains("sandbox-exec: sandbox_apply: Operation not permitted") { + return; + } + + assert_eq!( + Some(13), + output.status.code(), + "lsopen should remain denied even with bundle-scoped automation\nstdout: {}\nstderr: {stderr}", + String::from_utf8_lossy(&output.stdout), + ); + } + #[test] fn seatbelt_args_without_extension_profile_keep_legacy_preferences_read_access() { let cwd = std::env::temp_dir(); diff --git a/codex-rs/core/src/seatbelt_permissions.rs b/codex-rs/core/src/seatbelt_permissions.rs index 6b9fa6818..93bc0965a 100644 --- a/codex-rs/core/src/seatbelt_permissions.rs +++ b/codex-rs/core/src/seatbelt_permissions.rs @@ -82,7 +82,7 @@ pub(crate) fn build_seatbelt_extensions( MacOsAutomationPermission::BundleIds(bundle_ids) => { if !bundle_ids.is_empty() { clauses.push( - "(allow mach-lookup (global-name \"com.apple.coreservices.appleevents\"))" + "(allow mach-lookup\n (global-name \"com.apple.coreservices.launchservicesd\")\n (global-name \"com.apple.coreservices.appleevents\"))" .to_string(), ); let destinations = bundle_ids