Files
codex/codex-rs/core/src/seatbelt_permissions.rs
T
Michael Bolin 0c8a36676a fix: move inline codex-rs/core unit tests into sibling files (#14444)
## Why
PR #13783 moved the `codex.rs` unit tests into `codex_tests.rs`. This
applies the same extraction pattern across the rest of `codex-rs/core`
so the production modules stay focused on runtime code instead of large
inline test blocks.

Keeping the tests in sibling files also makes follow-up edits easier to
review because product changes no longer have to share a file with
hundreds or thousands of lines of test scaffolding.

## What changed
- replaced each inline `mod tests { ... }` in `codex-rs/core/src/**`
with a path-based module declaration
- moved each extracted unit test module into a sibling `*_tests.rs`
file, using `mod_tests.rs` for `mod.rs` modules
- preserved the existing `cfg(...)` guards and module-local structure so
the refactor remains structural rather than behavioral

## Testing
- `cargo test -p codex-core --lib` (`1653 passed; 0 failed; 5 ignored`)
- `just fix -p codex-core`
- `cargo fmt --check`
- `cargo shear`
2026-03-12 08:16:36 -07:00

193 lines
7.6 KiB
Rust

#![cfg(target_os = "macos")]
use std::collections::BTreeSet;
use std::path::PathBuf;
pub use codex_protocol::models::MacOsAutomationPermission;
pub use codex_protocol::models::MacOsContactsPermission;
pub use codex_protocol::models::MacOsPreferencesPermission;
pub use codex_protocol::models::MacOsSeatbeltProfileExtensions;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct SeatbeltExtensionPolicy {
pub(crate) policy: String,
pub(crate) dir_params: Vec<(String, PathBuf)>,
}
fn normalized_extensions(
extensions: &MacOsSeatbeltProfileExtensions,
) -> MacOsSeatbeltProfileExtensions {
let mut normalized = extensions.clone();
if let MacOsAutomationPermission::BundleIds(bundle_ids) = &extensions.macos_automation {
let bundle_ids = normalize_bundle_ids(bundle_ids);
normalized.macos_automation = if bundle_ids.is_empty() {
MacOsAutomationPermission::None
} else {
MacOsAutomationPermission::BundleIds(bundle_ids)
};
}
normalized
}
pub(crate) fn build_seatbelt_extensions(
extensions: &MacOsSeatbeltProfileExtensions,
) -> SeatbeltExtensionPolicy {
let extensions = normalized_extensions(extensions);
let mut clauses = Vec::new();
match extensions.macos_preferences {
MacOsPreferencesPermission::None => {}
MacOsPreferencesPermission::ReadOnly => {
clauses.push(
"(allow ipc-posix-shm-read* (ipc-posix-name-prefix \"apple.cfprefs.\"))"
.to_string(),
);
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.cfprefsd.daemon\")\n (global-name \"com.apple.cfprefsd.agent\")\n (local-name \"com.apple.cfprefsd.agent\"))"
.to_string(),
);
clauses.push("(allow user-preference-read)".to_string());
}
MacOsPreferencesPermission::ReadWrite => {
clauses.push(
"(allow ipc-posix-shm-read* (ipc-posix-name-prefix \"apple.cfprefs.\"))"
.to_string(),
);
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.cfprefsd.daemon\")\n (global-name \"com.apple.cfprefsd.agent\")\n (local-name \"com.apple.cfprefsd.agent\"))"
.to_string(),
);
clauses.push("(allow user-preference-read)".to_string());
clauses.push("(allow user-preference-write)".to_string());
clauses.push(
"(allow ipc-posix-shm-write-data (ipc-posix-name-prefix \"apple.cfprefs.\"))"
.to_string(),
);
clauses.push(
"(allow ipc-posix-shm-write-create (ipc-posix-name-prefix \"apple.cfprefs.\"))"
.to_string(),
);
}
}
match extensions.macos_automation {
MacOsAutomationPermission::None => {}
MacOsAutomationPermission::All => {
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.coreservices.appleevents\"))"
.to_string(),
);
clauses.push("(allow appleevent-send)".to_string());
}
MacOsAutomationPermission::BundleIds(bundle_ids) => {
if !bundle_ids.is_empty() {
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.coreservices.appleevents\"))"
.to_string(),
);
let destinations = bundle_ids
.iter()
.map(|bundle_id| format!(" (appleevent-destination \"{bundle_id}\")"))
.collect::<Vec<String>>()
.join("\n");
clauses.push(format!("(allow appleevent-send\n{destinations}\n)"));
}
}
}
if extensions.macos_launch_services {
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.coreservices.launchservicesd\")\n (global-name \"com.apple.lsd.mapdb\")\n (global-name \"com.apple.coreservices.quarantine-resolver\")\n (global-name \"com.apple.lsd.modifydb\"))"
.to_string(),
);
clauses.push("(allow lsopen)".to_string());
}
if extensions.macos_accessibility {
clauses.push("(allow mach-lookup (local-name \"com.apple.axserver\"))".to_string());
}
if extensions.macos_calendar {
clauses.push("(allow mach-lookup (global-name \"com.apple.CalendarAgent\"))".to_string());
}
if extensions.macos_reminders {
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.CalendarAgent\")\n (global-name \"com.apple.remindd\"))"
.to_string(),
);
}
let mut dir_params = Vec::new();
match extensions.macos_contacts {
MacOsContactsPermission::None => {}
MacOsContactsPermission::ReadOnly => {
clauses.push(
"(allow file-read* file-test-existence\n (subpath \"/System/Library/Address Book Plug-Ins\")\n (subpath (param \"ADDRESSBOOK_DIR\")))"
.to_string(),
);
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.tccd\")\n (global-name \"com.apple.tccd.system\")\n (global-name \"com.apple.contactsd.persistence\")\n (global-name \"com.apple.AddressBook.ContactsAccountsService\")\n (global-name \"com.apple.contacts.account-caching\")\n (global-name \"com.apple.accountsd.accountmanager\"))"
.to_string(),
);
if let Some(addressbook_dir) = addressbook_dir() {
dir_params.push(("ADDRESSBOOK_DIR".to_string(), addressbook_dir));
}
}
MacOsContactsPermission::ReadWrite => {
clauses.push(
"(allow file-read* file-write*\n (subpath \"/System/Library/Address Book Plug-Ins\")\n (subpath (param \"ADDRESSBOOK_DIR\"))\n (subpath \"/var/folders\")\n (subpath \"/private/var/folders\"))"
.to_string(),
);
clauses.push(
"(allow mach-lookup\n (global-name \"com.apple.tccd\")\n (global-name \"com.apple.tccd.system\")\n (global-name \"com.apple.contactsd.persistence\")\n (global-name \"com.apple.AddressBook.ContactsAccountsService\")\n (global-name \"com.apple.contacts.account-caching\")\n (global-name \"com.apple.accountsd.accountmanager\")\n (global-name \"com.apple.securityd.xpc\"))"
.to_string(),
);
if let Some(addressbook_dir) = addressbook_dir() {
dir_params.push(("ADDRESSBOOK_DIR".to_string(), addressbook_dir));
}
}
}
if clauses.is_empty() {
SeatbeltExtensionPolicy::default()
} else {
SeatbeltExtensionPolicy {
policy: format!(
"; macOS permission profile extensions\n{}\n",
clauses.join("\n")
),
dir_params,
}
}
}
fn addressbook_dir() -> Option<PathBuf> {
Some(dirs::home_dir()?.join("Library/Application Support/AddressBook"))
}
fn normalize_bundle_ids(bundle_ids: &[String]) -> Vec<String> {
let mut unique = BTreeSet::new();
for bundle_id in bundle_ids {
let candidate = bundle_id.trim();
if is_valid_bundle_id(candidate) {
unique.insert(candidate.to_string());
}
}
unique.into_iter().collect()
}
fn is_valid_bundle_id(bundle_id: &str) -> bool {
if bundle_id.len() < 3 || !bundle_id.contains('.') {
return false;
}
bundle_id
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-' || c == '_')
}
#[cfg(test)]
#[path = "seatbelt_permissions_tests.rs"]
mod tests;