From 984cb5b50189a2a26f0d1a6cfd3a3e3adffe1e1a Mon Sep 17 00:00:00 2001 From: eugene-yao-zocdoc Date: Fri, 13 Feb 2026 17:59:14 -0500 Subject: [PATCH 1/4] Add macOS sandbox support for notifications Add allowNotifications parameter to enable notification access for tools like terminal-notifier and osascript display notification. When enabled, grants access to: - Window server (com.apple.windowserver.active) - TCC system (com.apple.tccd.system) - Dock server (com.apple.dock.server) - Core services (coreservicesd, appleevents) - HID control and file issue extensions This allows sandboxed processes to display desktop notifications. Generated with AI Co-Authored-By: Claude Code --- src/sandbox/macos-sandbox-utils.ts | 17 +++++++++++++++++ src/sandbox/sandbox-config.ts | 6 ++++++ src/sandbox/sandbox-manager.ts | 5 +++++ 3 files changed, 28 insertions(+) diff --git a/src/sandbox/macos-sandbox-utils.ts b/src/sandbox/macos-sandbox-utils.ts index 9f816fd4..16dea522 100644 --- a/src/sandbox/macos-sandbox-utils.ts +++ b/src/sandbox/macos-sandbox-utils.ts @@ -35,6 +35,7 @@ export interface MacOSSandboxParams { allowGitConfig?: boolean enableWeakerNetworkIsolation?: boolean allowClipboard?: boolean + allowNotifications?: boolean binShell?: string } @@ -332,6 +333,7 @@ function generateSandboxProfile({ allowGitConfig = false, enableWeakerNetworkIsolation = false, allowClipboard = false, + allowNotifications = false, logTag, }: { readConfig: FsReadRestrictionConfig | undefined @@ -346,6 +348,7 @@ function generateSandboxProfile({ allowGitConfig?: boolean enableWeakerNetworkIsolation?: boolean allowClipboard?: boolean + allowNotifications?: boolean logTag: string }): string { const profile: string[] = [ @@ -499,6 +502,18 @@ function generateSandboxProfile({ ) } + // Notification access - required for terminal-notifier, osascript display notification, etc. + if (allowNotifications) { + profile.push( + '; Notification access (minimal: TCC and window server)', + '(allow mach-lookup', + ' (global-name "com.apple.tccd.system")', + ' (global-name "com.apple.windowserver.active")', + ')', + '', + ) + } + profile.push( '; File I/O on device files', '(allow file-ioctl (literal "/dev/null"))', @@ -655,6 +670,7 @@ export function wrapCommandWithSandboxMacOS( allowGitConfig = false, enableWeakerNetworkIsolation = false, allowClipboard = false, + allowNotifications = false, binShell, } = params @@ -688,6 +704,7 @@ export function wrapCommandWithSandboxMacOS( allowGitConfig, enableWeakerNetworkIsolation, allowClipboard, + allowNotifications, logTag, }) diff --git a/src/sandbox/sandbox-config.ts b/src/sandbox/sandbox-config.ts index eaed9db4..35399555 100644 --- a/src/sandbox/sandbox-config.ts +++ b/src/sandbox/sandbox-config.ts @@ -221,6 +221,12 @@ export const SandboxRuntimeConfigSchema = z.object({ .describe( 'Allow clipboard (pasteboard) access (macOS only). Required for pasting images into sandboxed processes.', ), + allowNotifications: z + .boolean() + .optional() + .describe( + 'Allow notification access (macOS only). Required for tools like terminal-notifier that need window server, TCC, and dock access.', + ), seccomp: SeccompConfigSchema.optional().describe( 'Custom seccomp binary paths (Linux only).', ), diff --git a/src/sandbox/sandbox-manager.ts b/src/sandbox/sandbox-manager.ts index 641c85dd..9efdc85f 100644 --- a/src/sandbox/sandbox-manager.ts +++ b/src/sandbox/sandbox-manager.ts @@ -471,6 +471,10 @@ function getAllowClipboard(): boolean { return config?.allowClipboard ?? false } +function getAllowNotifications(): boolean { + return config?.allowNotifications ?? false +} + function getSeccompConfig(): | { bpfPath?: string; applyPath?: string } | undefined { @@ -591,6 +595,7 @@ async function wrapWithSandbox( allowGitConfig: getAllowGitConfig(), enableWeakerNetworkIsolation: getEnableWeakerNetworkIsolation(), allowClipboard: getAllowClipboard(), + allowNotifications: getAllowNotifications(), binShell, }) From bdd71b6c7d1ccd800752a27c4c69f2f5f19451c6 Mon Sep 17 00:00:00 2001 From: eugene-yao-zocdoc Date: Tue, 17 Feb 2026 16:22:18 -0500 Subject: [PATCH 2/4] Scope notification sandbox rules to terminal-notifier via signing-identifier Instead of broadly allowing mach-lookup for notification services, use with-filter (signing-identifier "terminal-notifier") to restrict access to only the terminal-notifier binary. Also refines the set of allowed services to the minimal core notification pipeline. Generated with AI Co-Authored-By: Claude Code --- src/sandbox/macos-sandbox-utils.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/sandbox/macos-sandbox-utils.ts b/src/sandbox/macos-sandbox-utils.ts index 16dea522..3c42fe54 100644 --- a/src/sandbox/macos-sandbox-utils.ts +++ b/src/sandbox/macos-sandbox-utils.ts @@ -502,13 +502,19 @@ function generateSandboxProfile({ ) } - // Notification access - required for terminal-notifier, osascript display notification, etc. + // Notification access - SCOPED TO TERMINAL-NOTIFIER ONLY using signing-identifier if (allowNotifications) { profile.push( - '; Notification access (minimal: TCC and window server)', - '(allow mach-lookup', - ' (global-name "com.apple.tccd.system")', - ' (global-name "com.apple.windowserver.active")', + '; Notification access - scoped to terminal-notifier via signing-identifier', + '; Minimal set: only core notification pipeline services', + '(with-filter (signing-identifier "terminal-notifier")', + ' (allow mach-lookup', + ' (global-name "com.apple.usernoted.client")', + ' (global-name "com.apple.windowserver.active")', + ' (global-name "com.apple.tccd.system")', + ' (global-name "com.apple.CARenderServer")', + ' (global-name "com.apple.dock.server")', + ' )', ')', '', ) From 7cfce99db56762e072acebd33d85bd08d4d56a56 Mon Sep 17 00:00:00 2001 From: eugene-yao-zocdoc Date: Tue, 17 Feb 2026 18:17:29 -0500 Subject: [PATCH 3/4] Minimize notification permissions to only required services Systematically tested removing each mach-lookup permission one at a time. Only usernoted.client and windowserver.active are required for terminal-notifier to work. Removed tccd.system, CARenderServer, and dock.server which are not needed. Generated with AI Co-Authored-By: Claude Code --- src/sandbox/macos-sandbox-utils.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sandbox/macos-sandbox-utils.ts b/src/sandbox/macos-sandbox-utils.ts index 3c42fe54..a84093d9 100644 --- a/src/sandbox/macos-sandbox-utils.ts +++ b/src/sandbox/macos-sandbox-utils.ts @@ -511,9 +511,6 @@ function generateSandboxProfile({ ' (allow mach-lookup', ' (global-name "com.apple.usernoted.client")', ' (global-name "com.apple.windowserver.active")', - ' (global-name "com.apple.tccd.system")', - ' (global-name "com.apple.CARenderServer")', - ' (global-name "com.apple.dock.server")', ' )', ')', '', From 4e9631e64780a4918eca3910ca01e8e623717dd2 Mon Sep 17 00:00:00 2001 From: eugene-yao-zocdoc Date: Tue, 17 Feb 2026 19:51:33 -0500 Subject: [PATCH 4/4] Add audio hardware daemon mach services for sound notifications AudioComponentRegistrar and audiohald are needed for terminal-notifier to play notification sounds through the macOS audio subsystem. Generated with AI Co-Authored-By: Claude Code --- src/sandbox/macos-sandbox-utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sandbox/macos-sandbox-utils.ts b/src/sandbox/macos-sandbox-utils.ts index a84093d9..3174eec5 100644 --- a/src/sandbox/macos-sandbox-utils.ts +++ b/src/sandbox/macos-sandbox-utils.ts @@ -370,6 +370,8 @@ function generateSandboxProfile({ '', '; Mach IPC - specific services only (no wildcard)', '(allow mach-lookup', + ' (global-name "com.apple.audio.AudioComponentRegistrar")', + ' (global-name "com.apple.audio.audiohald")', ' (global-name "com.apple.audio.systemsoundserver")', ' (global-name "com.apple.distributed_notifications@Uv3")', ' (global-name "com.apple.FontObjectsServer")',