feat: Add additional macOS Sandbox Permissions for Launch Services, Contacts, Reminders (#14155)
Add additional macOS Sandbox Permissions levers for the following: - Launch Services - Contacts - Reminders
This commit is contained in:
parent
8ac27b2a16
commit
889b4796fc
25 changed files with 779 additions and 25 deletions
|
|
@ -39,15 +39,27 @@
|
|||
"calendar": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"contacts": {
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
},
|
||||
"launchServices": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"preferences": {
|
||||
"$ref": "#/definitions/MacOsPreferencesPermission"
|
||||
},
|
||||
"reminders": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"accessibility",
|
||||
"automations",
|
||||
"calendar",
|
||||
"preferences"
|
||||
"contacts",
|
||||
"launchServices",
|
||||
"preferences",
|
||||
"reminders"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
|
@ -324,6 +336,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
|
|||
|
|
@ -4044,6 +4044,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
@ -4070,6 +4078,18 @@
|
|||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"macos_contacts": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
}
|
||||
],
|
||||
"default": "none"
|
||||
},
|
||||
"macos_launch_services": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"macos_preferences": {
|
||||
"allOf": [
|
||||
{
|
||||
|
|
@ -4077,6 +4097,10 @@
|
|||
}
|
||||
],
|
||||
"default": "read_only"
|
||||
},
|
||||
"macos_reminders": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
|
|||
|
|
@ -39,15 +39,27 @@
|
|||
"calendar": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"contacts": {
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
},
|
||||
"launchServices": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"preferences": {
|
||||
"$ref": "#/definitions/MacOsPreferencesPermission"
|
||||
},
|
||||
"reminders": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"accessibility",
|
||||
"automations",
|
||||
"calendar",
|
||||
"preferences"
|
||||
"contacts",
|
||||
"launchServices",
|
||||
"preferences",
|
||||
"reminders"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
|
@ -124,6 +136,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
|
|||
|
|
@ -63,6 +63,22 @@
|
|||
"null"
|
||||
]
|
||||
},
|
||||
"contacts": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"launchServices": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"preferences": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
|
@ -72,6 +88,12 @@
|
|||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"reminders": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -138,6 +160,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
|
|||
|
|
@ -39,15 +39,27 @@
|
|||
"calendar": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"contacts": {
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
},
|
||||
"launchServices": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"preferences": {
|
||||
"$ref": "#/definitions/MacOsPreferencesPermission"
|
||||
},
|
||||
"reminders": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"accessibility",
|
||||
"automations",
|
||||
"calendar",
|
||||
"preferences"
|
||||
"contacts",
|
||||
"launchServices",
|
||||
"preferences",
|
||||
"reminders"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
|
@ -653,6 +665,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
|
|||
|
|
@ -35,15 +35,27 @@
|
|||
"calendar": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"contacts": {
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
},
|
||||
"launchServices": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"preferences": {
|
||||
"$ref": "#/definitions/MacOsPreferencesPermission"
|
||||
},
|
||||
"reminders": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"accessibility",
|
||||
"automations",
|
||||
"calendar",
|
||||
"preferences"
|
||||
"contacts",
|
||||
"launchServices",
|
||||
"preferences",
|
||||
"reminders"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
|
@ -5303,6 +5315,22 @@
|
|||
"null"
|
||||
]
|
||||
},
|
||||
"contacts": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"launchServices": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"preferences": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
|
@ -5312,6 +5340,12 @@
|
|||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"reminders": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
@ -5573,6 +5607,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
@ -5599,6 +5641,18 @@
|
|||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"macos_contacts": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
}
|
||||
],
|
||||
"default": "none"
|
||||
},
|
||||
"macos_launch_services": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"macos_preferences": {
|
||||
"allOf": [
|
||||
{
|
||||
|
|
@ -5606,6 +5660,10 @@
|
|||
}
|
||||
],
|
||||
"default": "read_only"
|
||||
},
|
||||
"macos_reminders": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
|
|||
|
|
@ -8070,6 +8070,14 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"MacOsContactsPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
"read_only",
|
||||
"read_write"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"MacOsPreferencesPermission": {
|
||||
"enum": [
|
||||
"none",
|
||||
|
|
@ -8096,6 +8104,18 @@
|
|||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"macos_contacts": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/MacOsContactsPermission"
|
||||
}
|
||||
],
|
||||
"default": "none"
|
||||
},
|
||||
"macos_launch_services": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"macos_preferences": {
|
||||
"allOf": [
|
||||
{
|
||||
|
|
@ -8103,6 +8123,10 @@
|
|||
}
|
||||
],
|
||||
"default": "read_only"
|
||||
},
|
||||
"macos_reminders": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type MacOsContactsPermission = "none" | "read_only" | "read_write";
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { MacOsAutomationPermission } from "./MacOsAutomationPermission";
|
||||
import type { MacOsContactsPermission } from "./MacOsContactsPermission";
|
||||
import type { MacOsPreferencesPermission } from "./MacOsPreferencesPermission";
|
||||
|
||||
export type MacOsSeatbeltProfileExtensions = { macos_preferences: MacOsPreferencesPermission, macos_automation: MacOsAutomationPermission, macos_accessibility: boolean, macos_calendar: boolean, };
|
||||
export type MacOsSeatbeltProfileExtensions = { macos_preferences: MacOsPreferencesPermission, macos_automation: MacOsAutomationPermission, macos_launch_services: boolean, macos_accessibility: boolean, macos_calendar: boolean, macos_reminders: boolean, macos_contacts: MacOsContactsPermission, };
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ export type { LocalShellAction } from "./LocalShellAction";
|
|||
export type { LocalShellExecAction } from "./LocalShellExecAction";
|
||||
export type { LocalShellStatus } from "./LocalShellStatus";
|
||||
export type { MacOsAutomationPermission } from "./MacOsAutomationPermission";
|
||||
export type { MacOsContactsPermission } from "./MacOsContactsPermission";
|
||||
export type { MacOsPreferencesPermission } from "./MacOsPreferencesPermission";
|
||||
export type { MacOsSeatbeltProfileExtensions } from "./MacOsSeatbeltProfileExtensions";
|
||||
export type { McpAuthStatus } from "./McpAuthStatus";
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { MacOsAutomationPermission } from "../MacOsAutomationPermission";
|
||||
import type { MacOsContactsPermission } from "../MacOsContactsPermission";
|
||||
import type { MacOsPreferencesPermission } from "../MacOsPreferencesPermission";
|
||||
|
||||
export type AdditionalMacOsPermissions = { preferences: MacOsPreferencesPermission, automations: MacOsAutomationPermission, accessibility: boolean, calendar: boolean, };
|
||||
export type AdditionalMacOsPermissions = { preferences: MacOsPreferencesPermission, automations: MacOsAutomationPermission, launchServices: boolean, accessibility: boolean, calendar: boolean, reminders: boolean, contacts: MacOsContactsPermission, };
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { MacOsAutomationPermission } from "../MacOsAutomationPermission";
|
||||
import type { MacOsContactsPermission } from "../MacOsContactsPermission";
|
||||
import type { MacOsPreferencesPermission } from "../MacOsPreferencesPermission";
|
||||
|
||||
export type GrantedMacOsPermissions = { preferences?: MacOsPreferencesPermission, automations?: MacOsAutomationPermission, accessibility?: boolean, calendar?: boolean, };
|
||||
export type GrantedMacOsPermissions = { preferences?: MacOsPreferencesPermission, automations?: MacOsAutomationPermission, launchServices?: boolean, accessibility?: boolean, calendar?: boolean, reminders?: boolean, contacts?: MacOsContactsPermission, };
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate;
|
|||
use codex_protocol::mcp::Tool as McpTool;
|
||||
use codex_protocol::models::FileSystemPermissions as CoreFileSystemPermissions;
|
||||
use codex_protocol::models::MacOsAutomationPermission as CoreMacOsAutomationPermission;
|
||||
use codex_protocol::models::MacOsContactsPermission as CoreMacOsContactsPermission;
|
||||
use codex_protocol::models::MacOsPreferencesPermission as CoreMacOsPreferencesPermission;
|
||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions as CoreMacOsSeatbeltProfileExtensions;
|
||||
use codex_protocol::models::MessagePhase;
|
||||
|
|
@ -973,8 +974,11 @@ impl From<AdditionalFileSystemPermissions> for CoreFileSystemPermissions {
|
|||
pub struct AdditionalMacOsPermissions {
|
||||
pub preferences: CoreMacOsPreferencesPermission,
|
||||
pub automations: CoreMacOsAutomationPermission,
|
||||
pub launch_services: bool,
|
||||
pub accessibility: bool,
|
||||
pub calendar: bool,
|
||||
pub reminders: bool,
|
||||
pub contacts: CoreMacOsContactsPermission,
|
||||
}
|
||||
|
||||
impl From<CoreMacOsSeatbeltProfileExtensions> for AdditionalMacOsPermissions {
|
||||
|
|
@ -982,8 +986,11 @@ impl From<CoreMacOsSeatbeltProfileExtensions> for AdditionalMacOsPermissions {
|
|||
Self {
|
||||
preferences: value.macos_preferences,
|
||||
automations: value.macos_automation,
|
||||
launch_services: value.macos_launch_services,
|
||||
accessibility: value.macos_accessibility,
|
||||
calendar: value.macos_calendar,
|
||||
reminders: value.macos_reminders,
|
||||
contacts: value.macos_contacts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -993,8 +1000,11 @@ impl From<AdditionalMacOsPermissions> for CoreMacOsSeatbeltProfileExtensions {
|
|||
Self {
|
||||
macos_preferences: value.preferences,
|
||||
macos_automation: value.automations,
|
||||
macos_launch_services: value.launch_services,
|
||||
macos_accessibility: value.accessibility,
|
||||
macos_calendar: value.calendar,
|
||||
macos_reminders: value.reminders,
|
||||
macos_contacts: value.contacts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1063,10 +1073,19 @@ pub struct GrantedMacOsPermissions {
|
|||
pub automations: Option<CoreMacOsAutomationPermission>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub launch_services: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub accessibility: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub calendar: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub reminders: Option<bool>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub contacts: Option<CoreMacOsContactsPermission>,
|
||||
}
|
||||
|
||||
impl From<GrantedMacOsPermissions> for CoreMacOsSeatbeltProfileExtensions {
|
||||
|
|
@ -1078,8 +1097,11 @@ impl From<GrantedMacOsPermissions> for CoreMacOsSeatbeltProfileExtensions {
|
|||
macos_automation: value
|
||||
.automations
|
||||
.unwrap_or(CoreMacOsAutomationPermission::None),
|
||||
macos_launch_services: value.launch_services.unwrap_or(false),
|
||||
macos_accessibility: value.accessibility.unwrap_or(false),
|
||||
macos_calendar: value.calendar.unwrap_or(false),
|
||||
macos_reminders: value.reminders.unwrap_or(false),
|
||||
macos_contacts: value.contacts.unwrap_or(CoreMacOsContactsPermission::None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1104,8 +1126,11 @@ impl From<GrantedPermissionProfile> for CorePermissionProfile {
|
|||
let macos = value.macos.and_then(|macos| {
|
||||
if macos.preferences.is_none()
|
||||
&& macos.automations.is_none()
|
||||
&& macos.launch_services.is_none()
|
||||
&& macos.accessibility.is_none()
|
||||
&& macos.calendar.is_none()
|
||||
&& macos.reminders.is_none()
|
||||
&& macos.contacts.is_none()
|
||||
{
|
||||
None
|
||||
} else {
|
||||
|
|
@ -5494,8 +5519,11 @@ mod tests {
|
|||
"automations": {
|
||||
"bundle_ids": ["com.apple.Notes"]
|
||||
},
|
||||
"launchServices": false,
|
||||
"accessibility": false,
|
||||
"calendar": false
|
||||
"calendar": false,
|
||||
"reminders": false,
|
||||
"contacts": "read_only"
|
||||
}
|
||||
},
|
||||
"skillMetadata": null,
|
||||
|
|
@ -5509,10 +5537,52 @@ mod tests {
|
|||
params
|
||||
.additional_permissions
|
||||
.and_then(|permissions| permissions.macos)
|
||||
.map(|macos| macos.automations),
|
||||
Some(CoreMacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]))
|
||||
.map(|macos| (macos.automations, macos.launch_services, macos.contacts)),
|
||||
Some((
|
||||
CoreMacOsAutomationPermission::BundleIds(vec!["com.apple.Notes".to_string(),]),
|
||||
false,
|
||||
CoreMacOsContactsPermission::ReadOnly,
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_execution_request_approval_accepts_macos_reminders_permission() {
|
||||
let params = serde_json::from_value::<CommandExecutionRequestApprovalParams>(json!({
|
||||
"threadId": "thr_123",
|
||||
"turnId": "turn_123",
|
||||
"itemId": "call_123",
|
||||
"command": "cat file",
|
||||
"cwd": "/tmp",
|
||||
"commandActions": null,
|
||||
"reason": null,
|
||||
"networkApprovalContext": null,
|
||||
"additionalPermissions": {
|
||||
"network": null,
|
||||
"fileSystem": null,
|
||||
"macos": {
|
||||
"preferences": "read_only",
|
||||
"automations": "none",
|
||||
"launchServices": false,
|
||||
"accessibility": false,
|
||||
"calendar": false,
|
||||
"reminders": true,
|
||||
"contacts": "none"
|
||||
}
|
||||
},
|
||||
"skillMetadata": null,
|
||||
"proposedExecpolicyAmendment": null,
|
||||
"proposedNetworkPolicyAmendments": null,
|
||||
"availableDecisions": null
|
||||
}))
|
||||
.expect("reminders permission should deserialize");
|
||||
|
||||
assert_eq!(
|
||||
params
|
||||
.additional_permissions
|
||||
.and_then(|permissions| permissions.macos)
|
||||
.map(|macos| macos.reminders),
|
||||
Some(true)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -5560,8 +5630,11 @@ mod tests {
|
|||
Some(CoreMacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: CoreMacOsPreferencesPermission::ReadOnly,
|
||||
macos_automation: CoreMacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: CoreMacOsContactsPermission::None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
|
|
@ -5581,8 +5654,29 @@ mod tests {
|
|||
macos_automation: CoreMacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: CoreMacOsContactsPermission::None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
json!({
|
||||
"launchServices": true,
|
||||
}),
|
||||
Some(GrantedMacOsPermissions {
|
||||
launch_services: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
Some(CoreMacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: CoreMacOsPreferencesPermission::None,
|
||||
macos_automation: CoreMacOsAutomationPermission::None,
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: CoreMacOsContactsPermission::None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
|
|
@ -5596,8 +5690,11 @@ mod tests {
|
|||
Some(CoreMacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: CoreMacOsPreferencesPermission::None,
|
||||
macos_automation: CoreMacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: CoreMacOsContactsPermission::None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
|
|
@ -5611,8 +5708,47 @@ mod tests {
|
|||
Some(CoreMacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: CoreMacOsPreferencesPermission::None,
|
||||
macos_automation: CoreMacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: CoreMacOsContactsPermission::None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
json!({
|
||||
"reminders": true,
|
||||
}),
|
||||
Some(GrantedMacOsPermissions {
|
||||
reminders: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
Some(CoreMacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: CoreMacOsPreferencesPermission::None,
|
||||
macos_automation: CoreMacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: true,
|
||||
macos_contacts: CoreMacOsContactsPermission::None,
|
||||
}),
|
||||
),
|
||||
(
|
||||
json!({
|
||||
"contacts": "read_only",
|
||||
}),
|
||||
Some(GrantedMacOsPermissions {
|
||||
contacts: Some(CoreMacOsContactsPermission::ReadOnly),
|
||||
..Default::default()
|
||||
}),
|
||||
Some(CoreMacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: CoreMacOsPreferencesPermission::None,
|
||||
macos_automation: CoreMacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: CoreMacOsContactsPermission::ReadOnly,
|
||||
}),
|
||||
),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2627,6 +2627,7 @@ mod tests {
|
|||
use codex_app_server_protocol::TurnPlanStepStatus;
|
||||
use codex_protocol::mcp::CallToolResult;
|
||||
use codex_protocol::models::MacOsAutomationPermission;
|
||||
use codex_protocol::models::MacOsContactsPermission;
|
||||
use codex_protocol::models::MacOsPreferencesPermission;
|
||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||
use codex_protocol::plan_tool::PlanItemArg;
|
||||
|
|
@ -2716,8 +2717,11 @@ mod tests {
|
|||
"com.apple.Notes".to_string(),
|
||||
"com.apple.Reminders".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::ReadWrite,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -2731,8 +2735,11 @@ mod tests {
|
|||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::ReadOnly,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
@ -2749,8 +2756,28 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
serde_json::json!({
|
||||
"launchServices": true,
|
||||
}),
|
||||
CorePermissionProfile {
|
||||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::None,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
@ -2763,8 +2790,11 @@ mod tests {
|
|||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::None,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
@ -2777,8 +2807,45 @@ mod tests {
|
|||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::None,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
serde_json::json!({
|
||||
"reminders": true,
|
||||
}),
|
||||
CorePermissionProfile {
|
||||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::None,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
serde_json::json!({
|
||||
"contacts": "read_only",
|
||||
}),
|
||||
CorePermissionProfile {
|
||||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::None,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::ReadOnly,
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,10 +33,16 @@ Seatbelt also supports macOS permission-profile extensions layered on top of
|
|||
enables broad Apple Events send permissions.
|
||||
- `macos_automation = ["com.apple.Notes", ...]`:
|
||||
enables Apple Events send only to listed bundle IDs.
|
||||
- `macos_launch_services = true`:
|
||||
enables LaunchServices lookups and open/launch operations.
|
||||
- `macos_accessibility = true`:
|
||||
enables `com.apple.axserver` mach lookup.
|
||||
- `macos_calendar = true`:
|
||||
enables `com.apple.CalendarAgent` mach lookup.
|
||||
- `macos_contacts = "read_only"`:
|
||||
enables Address Book read access and Contacts read services.
|
||||
- `macos_contacts = "read_write"`:
|
||||
includes the readonly Contacts clauses plus Address Book writes and keychain/temp helpers required for writes.
|
||||
|
||||
### Linux
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,19 @@
|
|||
(subpath "/System/iOSSupport/System/Library/SubFrameworks")
|
||||
(subpath "/usr/lib"))
|
||||
|
||||
; System Framework and AppKit resources
|
||||
(allow file-read* file-test-existence
|
||||
(subpath "/Library/Apple/System/Library/Frameworks")
|
||||
(subpath "/Library/Apple/System/Library/PrivateFrameworks")
|
||||
(subpath "/Library/Apple/usr/lib")
|
||||
(subpath "/System/Library/Frameworks")
|
||||
(subpath "/System/Library/PrivateFrameworks")
|
||||
(subpath "/System/Library/SubFrameworks")
|
||||
(subpath "/System/iOSSupport/System/Library/Frameworks")
|
||||
(subpath "/System/iOSSupport/System/Library/PrivateFrameworks")
|
||||
(subpath "/System/iOSSupport/System/Library/SubFrameworks")
|
||||
(subpath "/usr/lib"))
|
||||
|
||||
; Allow guarded vnodes.
|
||||
(allow system-mac-syscall (mac-policy-name "vnguard"))
|
||||
|
||||
|
|
@ -87,6 +100,11 @@
|
|||
(allow file-read* (subpath "/etc"))
|
||||
(allow file-read* (subpath "/private/etc"))
|
||||
|
||||
(allow file-read* file-test-existence
|
||||
(literal "/System/Library/CoreServices")
|
||||
(literal "/System/Library/CoreServices/.SystemVersionPlatform.plist")
|
||||
(literal "/System/Library/CoreServices/SystemVersion.plist"))
|
||||
|
||||
; Some processes read /var metadata during startup.
|
||||
(allow file-read-metadata (subpath "/var"))
|
||||
(allow file-read-metadata (subpath "/private/var"))
|
||||
|
|
@ -178,4 +196,4 @@
|
|||
|
||||
; App sandbox extensions
|
||||
(allow file-read* (extension "com.apple.app-sandbox.read"))
|
||||
(allow file-read* file-write* (extension "com.apple.app-sandbox.read-write"))
|
||||
(allow file-read* file-write* (extension "com.apple.app-sandbox.read-write"))
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use codex_protocol::models::MacOsAutomationPermission;
|
||||
use codex_protocol::models::MacOsContactsPermission;
|
||||
use codex_protocol::models::MacOsPreferencesPermission;
|
||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||
|
||||
|
|
@ -24,8 +25,14 @@ pub(crate) fn merge_macos_seatbelt_profile_extensions(
|
|||
&base.macos_automation,
|
||||
&permissions.macos_automation,
|
||||
),
|
||||
macos_launch_services: base.macos_launch_services || permissions.macos_launch_services,
|
||||
macos_accessibility: base.macos_accessibility || permissions.macos_accessibility,
|
||||
macos_calendar: base.macos_calendar || permissions.macos_calendar,
|
||||
macos_reminders: base.macos_reminders || permissions.macos_reminders,
|
||||
macos_contacts: union_macos_contacts_permission(
|
||||
&base.macos_contacts,
|
||||
&permissions.macos_contacts,
|
||||
),
|
||||
}),
|
||||
None => Some(permissions.clone()),
|
||||
}
|
||||
|
|
@ -45,8 +52,12 @@ pub(crate) fn intersect_macos_seatbelt_profile_extensions(
|
|||
Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: requested.macos_preferences.min(granted.macos_preferences),
|
||||
macos_automation,
|
||||
macos_launch_services: requested.macos_launch_services
|
||||
&& granted.macos_launch_services,
|
||||
macos_accessibility: requested.macos_accessibility && granted.macos_accessibility,
|
||||
macos_calendar: requested.macos_calendar && granted.macos_calendar,
|
||||
macos_reminders: requested.macos_reminders && granted.macos_reminders,
|
||||
macos_contacts: requested.macos_contacts.min(granted.macos_contacts),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
|
|
@ -68,6 +79,17 @@ fn union_macos_preferences_permission(
|
|||
}
|
||||
}
|
||||
|
||||
fn union_macos_contacts_permission(
|
||||
base: &MacOsContactsPermission,
|
||||
requested: &MacOsContactsPermission,
|
||||
) -> MacOsContactsPermission {
|
||||
if base < requested {
|
||||
requested.clone()
|
||||
} else {
|
||||
base.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Unions two automation permissions by keeping the more permissive result.
|
||||
///
|
||||
/// `All` wins over everything, `None` yields to the other side, and two bundle
|
||||
|
|
@ -133,8 +155,10 @@ mod tests {
|
|||
use super::intersect_macos_seatbelt_profile_extensions;
|
||||
use super::merge_macos_seatbelt_profile_extensions;
|
||||
use super::union_macos_automation_permission;
|
||||
use super::union_macos_contacts_permission;
|
||||
use super::union_macos_preferences_permission;
|
||||
use codex_protocol::models::MacOsAutomationPermission;
|
||||
use codex_protocol::models::MacOsContactsPermission;
|
||||
use codex_protocol::models::MacOsPreferencesPermission;
|
||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
|
@ -146,8 +170,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Calendar".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::ReadOnly,
|
||||
};
|
||||
let requested = MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::ReadWrite,
|
||||
|
|
@ -155,8 +182,11 @@ mod tests {
|
|||
"com.apple.Notes".to_string(),
|
||||
"com.apple.Calendar".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::ReadWrite,
|
||||
};
|
||||
|
||||
let merged =
|
||||
|
|
@ -170,8 +200,11 @@ mod tests {
|
|||
"com.apple.Calendar".to_string(),
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::ReadWrite,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -219,8 +252,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
};
|
||||
let granted = MacOsSeatbeltProfileExtensions::default();
|
||||
|
||||
|
|
@ -229,4 +265,14 @@ mod tests {
|
|||
|
||||
assert_eq!(intersected, Some(MacOsSeatbeltProfileExtensions::default()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_macos_contacts_permission_does_not_downgrade() {
|
||||
let base = MacOsContactsPermission::ReadWrite;
|
||||
let requested = MacOsContactsPermission::ReadOnly;
|
||||
|
||||
let merged = union_macos_contacts_permission(&base, &requested);
|
||||
|
||||
assert_eq!(merged, MacOsContactsPermission::ReadWrite);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -737,6 +737,8 @@ mod tests {
|
|||
#[cfg(target_os = "macos")]
|
||||
use codex_protocol::models::MacOsAutomationPermission;
|
||||
#[cfg(target_os = "macos")]
|
||||
use codex_protocol::models::MacOsContactsPermission;
|
||||
#[cfg(target_os = "macos")]
|
||||
use codex_protocol::models::MacOsPreferencesPermission;
|
||||
#[cfg(target_os = "macos")]
|
||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||
|
|
@ -981,8 +983,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -1013,8 +1018,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -1027,8 +1035,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
@ -1092,8 +1103,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Calendar".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
Some(&PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
|
|
@ -1105,8 +1119,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
|
|
@ -1120,8 +1137,11 @@ mod tests {
|
|||
"com.apple.Calendar".to_string(),
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ use codex_protocol::permissions::NetworkSandboxPolicy;
|
|||
|
||||
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_SEATBELT_PLATFORM_DEFAULTS: &str = include_str!("seatbelt_platform_defaults.sbpl");
|
||||
const MACOS_RESTRICTED_READ_ONLY_PLATFORM_DEFAULTS: &str =
|
||||
include_str!("restricted_read_only_platform_defaults.sbpl");
|
||||
|
||||
/// When working with `sandbox-exec`, only consider `sandbox-exec` in `/usr/bin`
|
||||
/// to defend against an attacker trying to inject a malicious version on the
|
||||
|
|
@ -529,7 +530,7 @@ pub(crate) fn create_seatbelt_command_args_for_policies_with_extensions(
|
|||
network_policy,
|
||||
];
|
||||
if include_platform_defaults {
|
||||
policy_sections.push(MACOS_SEATBELT_PLATFORM_DEFAULTS.to_string());
|
||||
policy_sections.push(MACOS_RESTRICTED_READ_ONLY_PLATFORM_DEFAULTS.to_string());
|
||||
}
|
||||
if !seatbelt_extensions.policy.is_empty() {
|
||||
policy_sections.push(seatbelt_extensions.policy.clone());
|
||||
|
|
@ -599,6 +600,7 @@ mod tests {
|
|||
use crate::protocol::SandboxPolicy;
|
||||
use crate::seatbelt::MACOS_PATH_TO_SEATBELT_EXECUTABLE;
|
||||
use crate::seatbelt_permissions::MacOsAutomationPermission;
|
||||
use crate::seatbelt_permissions::MacOsContactsPermission;
|
||||
use crate::seatbelt_permissions::MacOsPreferencesPermission;
|
||||
use crate::seatbelt_permissions::MacOsSeatbeltProfileExtensions;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
|
|
@ -787,8 +789,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
);
|
||||
let policy = &args[1];
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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;
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ pub(crate) fn build_seatbelt_extensions(
|
|||
MacOsAutomationPermission::None => {}
|
||||
MacOsAutomationPermission::All => {
|
||||
clauses.push(
|
||||
"(allow mach-lookup\n (global-name \"com.apple.coreservices.launchservicesd\")\n (global-name \"com.apple.coreservices.appleevents\"))"
|
||||
"(allow mach-lookup\n (global-name \"com.apple.coreservices.appleevents\"))"
|
||||
.to_string(),
|
||||
);
|
||||
clauses.push("(allow appleevent-send)".to_string());
|
||||
|
|
@ -82,7 +83,7 @@ pub(crate) fn build_seatbelt_extensions(
|
|||
MacOsAutomationPermission::BundleIds(bundle_ids) => {
|
||||
if !bundle_ids.is_empty() {
|
||||
clauses.push(
|
||||
"(allow mach-lookup\n (global-name \"com.apple.coreservices.launchservicesd\")\n (global-name \"com.apple.coreservices.appleevents\"))"
|
||||
"(allow mach-lookup\n (global-name \"com.apple.coreservices.appleevents\"))"
|
||||
.to_string(),
|
||||
);
|
||||
let destinations = bundle_ids
|
||||
|
|
@ -95,6 +96,14 @@ pub(crate) fn build_seatbelt_extensions(
|
|||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
@ -103,6 +112,44 @@ pub(crate) fn build_seatbelt_extensions(
|
|||
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 {
|
||||
|
|
@ -111,11 +158,15 @@ pub(crate) fn build_seatbelt_extensions(
|
|||
"; macOS permission profile extensions\n{}\n",
|
||||
clauses.join("\n")
|
||||
),
|
||||
dir_params: Vec::new(),
|
||||
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 {
|
||||
|
|
@ -139,6 +190,7 @@ fn is_valid_bundle_id(bundle_id: &str) -> bool {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::MacOsAutomationPermission;
|
||||
use super::MacOsContactsPermission;
|
||||
use super::MacOsPreferencesPermission;
|
||||
use super::MacOsSeatbeltProfileExtensions;
|
||||
use super::build_seatbelt_extensions;
|
||||
|
|
@ -173,11 +225,7 @@ mod tests {
|
|||
..Default::default()
|
||||
});
|
||||
assert!(policy.policy.contains("(allow appleevent-send)"));
|
||||
assert!(
|
||||
policy
|
||||
.policy
|
||||
.contains("com.apple.coreservices.launchservicesd")
|
||||
);
|
||||
assert!(policy.policy.contains("com.apple.coreservices.appleevents"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -202,6 +250,28 @@ mod tests {
|
|||
.contains("(appleevent-destination \"com.apple.Notes\")")
|
||||
);
|
||||
assert!(!policy.policy.contains("bad bundle"));
|
||||
assert!(policy.policy.contains("com.apple.coreservices.appleevents"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn launch_services_emit_launch_clauses() {
|
||||
let policy = build_seatbelt_extensions(&MacOsSeatbeltProfileExtensions {
|
||||
macos_launch_services: true,
|
||||
..Default::default()
|
||||
});
|
||||
assert!(
|
||||
policy
|
||||
.policy
|
||||
.contains("com.apple.coreservices.launchservicesd")
|
||||
);
|
||||
assert!(policy.policy.contains("com.apple.lsd.mapdb"));
|
||||
assert!(
|
||||
policy
|
||||
.policy
|
||||
.contains("com.apple.coreservices.quarantine-resolver")
|
||||
);
|
||||
assert!(policy.policy.contains("com.apple.lsd.modifydb"));
|
||||
assert!(policy.policy.contains("(allow lsopen)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -215,6 +285,56 @@ mod tests {
|
|||
assert!(policy.policy.contains("com.apple.CalendarAgent"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reminders_emit_calendar_agent_and_remindd_lookups() {
|
||||
let policy = build_seatbelt_extensions(&MacOsSeatbeltProfileExtensions {
|
||||
macos_reminders: true,
|
||||
..Default::default()
|
||||
});
|
||||
assert!(policy.policy.contains("com.apple.CalendarAgent"));
|
||||
assert!(policy.policy.contains("com.apple.remindd"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contacts_read_only_emit_contacts_read_clauses() {
|
||||
let policy = build_seatbelt_extensions(&MacOsSeatbeltProfileExtensions {
|
||||
macos_contacts: MacOsContactsPermission::ReadOnly,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
assert!(
|
||||
policy
|
||||
.policy
|
||||
.contains("(subpath \"/System/Library/Address Book Plug-Ins\")")
|
||||
);
|
||||
assert!(
|
||||
policy
|
||||
.policy
|
||||
.contains("(subpath (param \"ADDRESSBOOK_DIR\"))")
|
||||
);
|
||||
assert!(policy.policy.contains("com.apple.contactsd.persistence"));
|
||||
assert!(policy.policy.contains("com.apple.accountsd.accountmanager"));
|
||||
assert!(!policy.policy.contains("com.apple.securityd.xpc"));
|
||||
assert!(
|
||||
policy
|
||||
.dir_params
|
||||
.iter()
|
||||
.any(|(key, _)| key == "ADDRESSBOOK_DIR")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contacts_read_write_emit_write_clauses() {
|
||||
let policy = build_seatbelt_extensions(&MacOsSeatbeltProfileExtensions {
|
||||
macos_contacts: MacOsContactsPermission::ReadWrite,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
assert!(policy.policy.contains("(subpath \"/var/folders\")"));
|
||||
assert!(policy.policy.contains("(subpath \"/private/var/folders\")"));
|
||||
assert!(policy.policy.contains("com.apple.securityd.xpc"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_extensions_emit_preferences_read_only_policy() {
|
||||
let policy = build_seatbelt_extensions(&MacOsSeatbeltProfileExtensions::default());
|
||||
|
|
|
|||
|
|
@ -867,6 +867,7 @@ mod tests {
|
|||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::models::FileSystemPermissions;
|
||||
use codex_protocol::models::MacOsAutomationPermission;
|
||||
use codex_protocol::models::MacOsContactsPermission;
|
||||
use codex_protocol::models::MacOsPreferencesPermission;
|
||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
|
|
@ -1466,6 +1467,7 @@ permissions:
|
|||
macos_preferences: "read_write"
|
||||
macos_automation:
|
||||
- "com.apple.Notes"
|
||||
macos_launch_services: true
|
||||
macos_accessibility: true
|
||||
macos_calendar: true
|
||||
"#,
|
||||
|
|
@ -1480,8 +1482,39 @@ permissions:
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skill_metadata_parses_macos_reminders_permission_yaml() {
|
||||
let parsed = serde_yaml::from_str::<SkillMetadataFile>(
|
||||
r#"
|
||||
permissions:
|
||||
macos:
|
||||
macos_reminders: true
|
||||
"#,
|
||||
)
|
||||
.expect("parse reminders skill metadata");
|
||||
|
||||
assert_eq!(
|
||||
parsed.permissions,
|
||||
Some(PermissionProfile {
|
||||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::ReadOnly,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -1503,6 +1536,7 @@ permissions:
|
|||
macos_preferences: "read_write"
|
||||
macos_automation:
|
||||
- "com.apple.Notes"
|
||||
macos_launch_services: true
|
||||
macos_accessibility: true
|
||||
macos_calendar: true
|
||||
"#,
|
||||
|
|
@ -1525,8 +1559,11 @@ permissions:
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string()
|
||||
],),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
@ -1548,6 +1585,7 @@ permissions:
|
|||
macos_preferences: "read_write"
|
||||
macos_automation:
|
||||
- "com.apple.Notes"
|
||||
macos_launch_services: true
|
||||
macos_accessibility: true
|
||||
macos_calendar: true
|
||||
"#,
|
||||
|
|
@ -1570,8 +1608,11 @@ permissions:
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string()
|
||||
],),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -657,6 +657,7 @@ async fn prepare_escalated_exec_permission_profile_unions_turn_and_requested_mac
|
|||
PermissionProfile {
|
||||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
|
|
|
|||
|
|
@ -110,6 +110,28 @@ pub enum MacOsPreferencesPermission {
|
|||
ReadWrite,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Default,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
JsonSchema,
|
||||
TS,
|
||||
)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MacOsContactsPermission {
|
||||
#[default]
|
||||
None,
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash, Serialize, Deserialize, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case", try_from = "MacOsAutomationPermissionDe")]
|
||||
pub enum MacOsAutomationPermission {
|
||||
|
|
@ -174,10 +196,16 @@ pub struct MacOsSeatbeltProfileExtensions {
|
|||
pub macos_preferences: MacOsPreferencesPermission,
|
||||
#[serde(alias = "automations")]
|
||||
pub macos_automation: MacOsAutomationPermission,
|
||||
#[serde(alias = "launch_services")]
|
||||
pub macos_launch_services: bool,
|
||||
#[serde(alias = "accessibility")]
|
||||
pub macos_accessibility: bool,
|
||||
#[serde(alias = "calendar")]
|
||||
pub macos_calendar: bool,
|
||||
#[serde(alias = "reminders")]
|
||||
pub macos_reminders: bool,
|
||||
#[serde(alias = "contacts")]
|
||||
pub macos_contacts: MacOsContactsPermission,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TS)]
|
||||
|
|
@ -1456,6 +1484,12 @@ mod tests {
|
|||
assert!(MacOsPreferencesPermission::ReadOnly < MacOsPreferencesPermission::ReadWrite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macos_contacts_permission_order_matches_permissiveness() {
|
||||
assert!(MacOsContactsPermission::None < MacOsContactsPermission::ReadOnly);
|
||||
assert!(MacOsContactsPermission::ReadOnly < MacOsContactsPermission::ReadWrite);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn permission_profile_deserializes_macos_seatbelt_profile_extensions() {
|
||||
let permission_profile = serde_json::from_value::<PermissionProfile>(serde_json::json!({
|
||||
|
|
@ -1464,6 +1498,7 @@ mod tests {
|
|||
"macos": {
|
||||
"macos_preferences": "read_write",
|
||||
"macos_automation": ["com.apple.Notes"],
|
||||
"macos_launch_services": true,
|
||||
"macos_accessibility": true,
|
||||
"macos_calendar": true
|
||||
}
|
||||
|
|
@ -1480,8 +1515,38 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn permission_profile_deserializes_macos_reminders_permission() {
|
||||
let permission_profile = serde_json::from_value::<PermissionProfile>(serde_json::json!({
|
||||
"macos": {
|
||||
"macos_reminders": true
|
||||
}
|
||||
}))
|
||||
.expect("deserialize reminders permission profile");
|
||||
|
||||
assert_eq!(
|
||||
permission_profile,
|
||||
PermissionProfile {
|
||||
network: None,
|
||||
file_system: None,
|
||||
macos: Some(MacOsSeatbeltProfileExtensions {
|
||||
macos_preferences: MacOsPreferencesPermission::ReadOnly,
|
||||
macos_automation: MacOsAutomationPermission::None,
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
|
@ -1502,8 +1567,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: false,
|
||||
macos_calendar: false,
|
||||
macos_reminders: false,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -1514,8 +1582,11 @@ mod tests {
|
|||
serde_json::from_value::<MacOsSeatbeltProfileExtensions>(serde_json::json!({
|
||||
"preferences": "read_write",
|
||||
"automations": ["com.apple.Notes"],
|
||||
"launch_services": true,
|
||||
"accessibility": true,
|
||||
"calendar": true
|
||||
"calendar": true,
|
||||
"reminders": true,
|
||||
"contacts": "read_only"
|
||||
}))
|
||||
.expect("deserialize macos permissions");
|
||||
|
||||
|
|
@ -1526,8 +1597,11 @@ mod tests {
|
|||
macos_automation: MacOsAutomationPermission::BundleIds(vec![
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: true,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::ReadOnly,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ use codex_core::features::Features;
|
|||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::mcp::RequestId;
|
||||
use codex_protocol::models::MacOsAutomationPermission;
|
||||
use codex_protocol::models::MacOsContactsPermission;
|
||||
use codex_protocol::models::MacOsPreferencesPermission;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::protocol::ElicitationAction;
|
||||
|
|
@ -800,6 +801,17 @@ pub(crate) fn format_additional_permissions_rule(
|
|||
if macos.macos_calendar {
|
||||
parts.push("macOS calendar".to_string());
|
||||
}
|
||||
if macos.macos_reminders {
|
||||
parts.push("macOS reminders".to_string());
|
||||
}
|
||||
if !matches!(macos.macos_contacts, MacOsContactsPermission::None) {
|
||||
let value = match macos.macos_contacts {
|
||||
MacOsContactsPermission::None => "none",
|
||||
MacOsContactsPermission::ReadOnly => "readonly",
|
||||
MacOsContactsPermission::ReadWrite => "readwrite",
|
||||
};
|
||||
parts.push(format!("macOS contacts {value}"));
|
||||
}
|
||||
}
|
||||
|
||||
if parts.is_empty() {
|
||||
|
|
@ -1401,8 +1413,11 @@ mod tests {
|
|||
"com.apple.Calendar".to_string(),
|
||||
"com.apple.Notes".to_string(),
|
||||
]),
|
||||
macos_launch_services: false,
|
||||
macos_accessibility: true,
|
||||
macos_calendar: true,
|
||||
macos_reminders: true,
|
||||
macos_contacts: MacOsContactsPermission::None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ expression: "render_overlay_lines(&view, 120)"
|
|||
Reason: need macOS automation
|
||||
|
||||
Permission rule: macOS preferences readwrite; macOS automation com.apple.Calendar, com.apple.Notes; macOS
|
||||
accessibility; macOS calendar
|
||||
accessibility; macOS calendar; macOS reminders
|
||||
|
||||
$ osascript -e 'tell application'
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue