From 2bb89dcc6c028cf583cbf96a85b0f4e19c318eaa Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 09:01:47 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]=20Fi?= =?UTF-8?q?x=20NSWorkspace.shared.open=20insecure=20scheme=20execution=20i?= =?UTF-8?q?n=20TriggerKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .Jules/sentinel.md | 4 ++++ .../TriggerKitCore/AutomationProgram+Validation.swift | 7 +++++++ .../Sources/TriggerKitRuntime/AutomationExecutor.swift | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/.Jules/sentinel.md b/.Jules/sentinel.md index 64a776d..3db26d9 100644 --- a/.Jules/sentinel.md +++ b/.Jules/sentinel.md @@ -6,3 +6,7 @@ **Vulnerability:** The app allowed opening any URL string without validating the scheme, making it possible for attackers to use NSWorkspace.shared.open with potentially dangerous handlers like file:// or system preference handlers. **Learning:** Passing untrusted URLs to NSWorkspace.shared.open or /usr/bin/open acts similarly to a shell execution if non-standard or local file schemes are used. It bypasses normal app boundaries. **Prevention:** Always restrict URL schemes to an explicit allowlist (like http, https) before delegating to system URL openers. +## 2026-06-16 - [Dangerous URL Scheme execution in automation frameworks] +**Vulnerability:** Although an explicit scheme allowlist is the strongest defense, applying it blindly to a customizable automation framework (like `TriggerKit`) broke critical user functionality relying on standard custom schemes (like `shortcuts://` or `mailto:`). This forced us to roll back a naive allowlist. +**Learning:** In contexts like scripting or macro execution where flexibility is a core requirement, an allowlist can cause severe functional regressions. We must fallback to an explicit blocklist of definitively dangerous system schemes (e.g. `file`, `x-apple.systempreferences`) to mitigate the `NSWorkspace.shared.open` sandbox escape vector without breaking valid user automations. +**Prevention:** Consider the product requirements before applying restrictive allowlists. Use a blocklist targeting known unsafe execution vectors (file://, system preferences) for generalized scripting engines where an allowlist isn't feasible. diff --git a/TriggerKit/Sources/TriggerKitCore/AutomationProgram+Validation.swift b/TriggerKit/Sources/TriggerKitCore/AutomationProgram+Validation.swift index 59684c4..1d9682a 100644 --- a/TriggerKit/Sources/TriggerKitCore/AutomationProgram+Validation.swift +++ b/TriggerKit/Sources/TriggerKitCore/AutomationProgram+Validation.swift @@ -207,6 +207,13 @@ private extension AutomationStep { if let allowedSchemes, !allowedSchemes.contains(scheme) { return [issue(disallowedCode, index: index, message: "\(label) scheme is not allowed: \(scheme)")] } + + // Security: Block dangerous URL schemes that can bypass app sandboxing or manipulate system settings. + let blockedSchemes: Set = ["file", "x-apple.systempreferences"] + if blockedSchemes.contains(scheme) { + return [issue(disallowedCode, index: index, message: "\(label) scheme is dangerously blocked: \(scheme)")] + } + return [] } diff --git a/TriggerKit/Sources/TriggerKitRuntime/AutomationExecutor.swift b/TriggerKit/Sources/TriggerKitRuntime/AutomationExecutor.swift index 73d3fdd..e0083ae 100644 --- a/TriggerKit/Sources/TriggerKitRuntime/AutomationExecutor.swift +++ b/TriggerKit/Sources/TriggerKitRuntime/AutomationExecutor.swift @@ -450,6 +450,14 @@ public final class AutomationExecutor { if let allowedSchemes, !allowedSchemes.contains(scheme) { return .failure("URL scheme not allowed: \(scheme)") } + + // Security: Block dangerous URL schemes that can bypass app sandboxing or manipulate system settings. + // These schemes act similarly to shell execution if passed untrusted inputs. + let blockedSchemes: Set = ["file", "x-apple.systempreferences"] + if blockedSchemes.contains(scheme) { + return .failure("Dangerous URL scheme blocked: \(scheme)") + } + guard NSWorkspace.shared.open(url) else { return .failure("Could not open URL: \(urlString)") }