cli: add macOS seatbelt debug flags for Mach services and Apple events

This commit is contained in:
Conrad Kramer
2026-06-04 19:54:15 -07:00
committed by Greg
parent 4de7a2b9d8
commit 186ed88fa1
5 changed files with 348 additions and 21 deletions
+63 -21
View File
@@ -49,6 +49,9 @@ pub async fn run_command_under_seatbelt(
config_profile: _,
cwd,
include_managed_config,
allow_mach_services,
allow_appleevent_bundle_ids,
allow_lsopen,
allow_unix_sockets,
log_denials,
config_overrides,
@@ -58,8 +61,8 @@ pub async fn run_command_under_seatbelt(
&permissions_profile,
include_managed_config,
);
run_command_under_sandbox(
DebugSandboxConfigOptions {
run_command_under_sandbox(RunCommandUnderSandboxParams {
config_options: DebugSandboxConfigOptions {
permissions_profile,
cwd,
managed_requirements_mode,
@@ -68,10 +71,13 @@ pub async fn run_command_under_seatbelt(
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Seatbelt,
sandbox_type: SandboxType::Seatbelt,
log_denials,
&allow_unix_sockets,
)
allow_mach_services: &allow_mach_services,
allow_appleevent_bundle_ids: &allow_appleevent_bundle_ids,
allow_lsopen,
allow_unix_sockets: &allow_unix_sockets,
})
.await
}
@@ -101,8 +107,8 @@ pub async fn run_command_under_landlock(
&permissions_profile,
include_managed_config,
);
run_command_under_sandbox(
DebugSandboxConfigOptions {
run_command_under_sandbox(RunCommandUnderSandboxParams {
config_options: DebugSandboxConfigOptions {
permissions_profile,
cwd,
managed_requirements_mode,
@@ -111,10 +117,13 @@ pub async fn run_command_under_landlock(
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Landlock,
/*log_denials*/ false,
&[],
)
sandbox_type: SandboxType::Landlock,
log_denials: false,
allow_mach_services: &[],
allow_appleevent_bundle_ids: &[],
allow_lsopen: false,
allow_unix_sockets: &[],
})
.await
}
@@ -135,8 +144,8 @@ pub async fn run_command_under_windows_sandbox(
&permissions_profile,
include_managed_config,
);
run_command_under_sandbox(
DebugSandboxConfigOptions {
run_command_under_sandbox(RunCommandUnderSandboxParams {
config_options: DebugSandboxConfigOptions {
permissions_profile,
cwd,
managed_requirements_mode,
@@ -145,10 +154,13 @@ pub async fn run_command_under_windows_sandbox(
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Windows,
/*log_denials*/ false,
&[],
)
sandbox_type: SandboxType::Windows,
log_denials: false,
allow_mach_services: &[],
allow_appleevent_bundle_ids: &[],
allow_lsopen: false,
allow_unix_sockets: &[],
})
.await
}
@@ -186,16 +198,43 @@ impl ManagedRequirementsMode {
}
}
async fn run_command_under_sandbox(
struct RunCommandUnderSandboxParams<'a> {
config_options: DebugSandboxConfigOptions,
command: Vec<String>,
config_overrides: CliConfigOverrides,
codex_linux_sandbox_exe: Option<PathBuf>,
sandbox_type: SandboxType,
log_denials: bool,
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
allow_unix_sockets: &[AbsolutePathBuf],
) -> anyhow::Result<()> {
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
allow_mach_services: &'a [String],
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
allow_appleevent_bundle_ids: &'a [String],
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
allow_lsopen: bool,
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
allow_unix_sockets: &'a [AbsolutePathBuf],
}
async fn run_command_under_sandbox(params: RunCommandUnderSandboxParams<'_>) -> anyhow::Result<()> {
let RunCommandUnderSandboxParams {
config_options,
command,
config_overrides,
codex_linux_sandbox_exe,
sandbox_type,
log_denials,
allow_mach_services,
allow_appleevent_bundle_ids,
allow_lsopen,
allow_unix_sockets,
} = params;
#[cfg(not(target_os = "macos"))]
let _ = (
allow_mach_services,
allow_appleevent_bundle_ids,
allow_lsopen,
allow_unix_sockets,
);
let config = load_debug_sandbox_config(
config_overrides
.parse_overrides()
@@ -280,6 +319,9 @@ async fn run_command_under_sandbox(
sandbox_policy_cwd: sandbox_policy_cwd.as_path(),
enforce_managed_network: false,
network: network.as_ref(),
extra_mach_services: allow_mach_services,
extra_appleevent_bundle_ids: allow_appleevent_bundle_ids,
allow_lsopen,
extra_allow_unix_sockets: allow_unix_sockets,
});
spawn_debug_sandbox_child(
+63
View File
@@ -50,6 +50,18 @@ pub struct SeatbeltCommand {
)]
pub include_managed_config: bool,
/// Allow the sandboxed command to look up this Mach service name. Repeat to allow multiple services.
#[arg(long = "allow-mach-service", value_name = "SERVICE", value_parser = parse_non_empty_string)]
pub allow_mach_services: Vec<String>,
/// Allow the sandboxed command to send AppleEvents to this destination bundle ID. Repeat to allow multiple destinations.
#[arg(long = "allow-appleevent-destination", value_name = "BUNDLE_ID", value_parser = parse_non_empty_string)]
pub allow_appleevent_bundle_ids: Vec<String>,
/// Allow the sandboxed command to use LaunchServices open APIs.
#[arg(long = "allow-lsopen", default_value_t = false)]
pub allow_lsopen: bool,
/// Allow the sandboxed command to bind/connect AF_UNIX sockets rooted at this path. Relative paths are resolved against the current directory. Repeat to allow multiple paths.
#[arg(long = "allow-unix-socket", value_parser = parse_allow_unix_socket_path)]
pub allow_unix_sockets: Vec<AbsolutePathBuf>,
@@ -71,6 +83,15 @@ fn parse_allow_unix_socket_path(raw: &str) -> Result<AbsolutePathBuf, String> {
.map_err(|err| format!("invalid path {raw}: {err}"))
}
fn parse_non_empty_string(raw: &str) -> Result<String, String> {
let trimmed = raw.trim();
if trimmed.is_empty() {
Err("value must not be empty".to_string())
} else {
Ok(trimmed.to_string())
}
}
#[derive(Debug, Parser)]
pub struct LandlockCommand {
/// Named permissions profile to apply from the active configuration stack.
@@ -140,3 +161,45 @@ pub struct WindowsCommand {
#[arg(trailing_var_arg = true)]
pub command: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::SeatbeltCommand;
use clap::Parser;
use pretty_assertions::assert_eq;
#[test]
fn seatbelt_command_parses_additional_allowlist_flags() {
let command = SeatbeltCommand::try_parse_from([
"seatbelt",
"--allow-mach-service",
"com.apple.foo",
"--allow-mach-service",
"com.apple.bar",
"--allow-appleevent-destination",
"com.apple.finder",
"--allow-lsopen",
"--allow-unix-socket",
"/tmp/codex.sock",
"--",
"/bin/echo",
"hi",
])
.expect("parse");
assert_eq!(
command.allow_mach_services,
vec!["com.apple.foo".to_string(), "com.apple.bar".to_string()]
);
assert_eq!(
command.allow_appleevent_bundle_ids,
vec!["com.apple.finder".to_string()]
);
assert!(command.allow_lsopen);
assert_eq!(command.allow_unix_sockets.len(), 1);
assert_eq!(
command.command,
vec!["/bin/echo".to_string(), "hi".to_string()]
);
}
}