core/protocol: add structured macOS additional permissions and merge them into sandbox execution (#13499)

## Summary
- Introduce strongly-typed macOS additional permissions across
protocol/core/app-server boundaries.
- Merge additional permissions into effective sandbox execution,
including macOS seatbelt profile extensions.
- Expand docs, schema/tool definitions, UI rendering, and tests for
`network`, `file_system`, and `macos` additional permissions.
This commit is contained in:
Celia Chen
2026-03-05 16:21:45 -08:00
committed by GitHub
Unverified
parent 4e77ea0ec7
commit aaefee04cd
24 changed files with 1013 additions and 379 deletions
@@ -19,6 +19,8 @@ use crate::render::renderable::Renderable;
use codex_core::features::Features;
use codex_protocol::ThreadId;
use codex_protocol::mcp::RequestId;
use codex_protocol::models::MacOsAutomationPermission;
use codex_protocol::models::MacOsPreferencesPermission;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::ElicitationAction;
use codex_protocol::protocol::FileChange;
@@ -668,6 +670,36 @@ fn format_additional_permissions_rule(
parts.push(format!("write {writes}"));
}
}
if let Some(macos) = additional_permissions.macos.as_ref() {
if !matches!(
macos.macos_preferences,
MacOsPreferencesPermission::ReadOnly
) {
let value = match macos.macos_preferences {
MacOsPreferencesPermission::ReadOnly => "readonly",
MacOsPreferencesPermission::ReadWrite => "readwrite",
MacOsPreferencesPermission::None => "none",
};
parts.push(format!("macOS preferences {value}"));
}
match &macos.macos_automation {
MacOsAutomationPermission::All => {
parts.push("macOS automation all".to_string());
}
MacOsAutomationPermission::BundleIds(bundle_ids) => {
if !bundle_ids.is_empty() {
parts.push(format!("macOS automation {}", bundle_ids.join(", ")));
}
}
MacOsAutomationPermission::None => {}
}
if macos.macos_accessibility {
parts.push("macOS accessibility".to_string());
}
if macos.macos_calendar {
parts.push("macOS calendar".to_string());
}
}
if parts.is_empty() {
None
@@ -727,6 +759,9 @@ mod tests {
use super::*;
use crate::app_event::AppEvent;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::MacOsAutomationPermission;
use codex_protocol::models::MacOsPreferencesPermission;
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
use codex_protocol::models::NetworkPermissions;
use codex_protocol::protocol::ExecPolicyAmendment;
use codex_protocol::protocol::NetworkApprovalProtocol;
@@ -1150,6 +1185,39 @@ mod tests {
);
}
#[test]
fn additional_permissions_macos_prompt_snapshot() {
let (tx, _rx) = unbounded_channel::<AppEvent>();
let tx = AppEventSender::new(tx);
let exec_request = ApprovalRequest::Exec {
thread_id: ThreadId::new(),
thread_label: None,
id: "test".into(),
command: vec!["osascript".into(), "-e".into(), "tell application".into()],
reason: Some("need macOS automation".into()),
available_decisions: vec![ReviewDecision::Approved, ReviewDecision::Abort],
network_approval_context: None,
additional_permissions: Some(PermissionProfile {
macos: Some(MacOsSeatbeltProfileExtensions {
macos_preferences: MacOsPreferencesPermission::ReadWrite,
macos_automation: MacOsAutomationPermission::BundleIds(vec![
"com.apple.Calendar".to_string(),
"com.apple.Notes".to_string(),
]),
macos_accessibility: true,
macos_calendar: true,
}),
..Default::default()
}),
};
let view = ApprovalOverlay::new(exec_request, tx, Features::with_defaults());
assert_snapshot!(
"approval_overlay_additional_permissions_macos_prompt",
render_overlay_lines(&view, 120)
);
}
#[test]
fn network_exec_prompt_title_includes_host() {
let (tx, _rx) = unbounded_channel::<AppEvent>();
@@ -0,0 +1,18 @@
---
source: tui/src/bottom_pane/approval_overlay.rs
expression: "render_overlay_lines(&view, 120)"
---
Would you like to run the following command?
Reason: need macOS automation
Permission rule: macOS preferences readwrite; macOS automation com.apple.Calendar, com.apple.Notes; macOS
accessibility; macOS calendar
$ osascript -e 'tell application'
1. Yes, proceed (y)
2. No, and tell Codex what to do differently (esc)
Press enter to confirm or esc to cancel