Block state-changing debug deep links#15
Conversation
- allowsRemoteInvocation computed property on GooseDebugCommandDefinition: only 'read' and 'keyed read' risk commands can be invoked via URL scheme - handleDebugCommandDeepLink validates command exists and allowsRemoteInvocation before executing; unknown or state-changing commands are blocked + logged - remoteURLExample returns 'Remote invocation disabled' for blocked commands Ported from b-nnett#15 (kobemartin)
|
|
||
| var allowsRemoteInvocation: Bool { | ||
| risk == "read" || risk == "keyed read" | ||
| } |
There was a problem hiding this comment.
Stringly-typed allowlist fails open on misclassification.
risk == "read" || risk == "keyed read" means a typo in a future command's risk field — or a new command added with a mislabeled string — silently becomes remotely invokable. There is no compiler check that the two literal strings are the only safe values.
Suggested fix: convert risk to a CommandRisk enum:
enum CommandRisk: String {
case read = "read"
case keyedRead = "keyed read"
case write
case unknown
}
var allowsRemoteInvocation: Bool {
risk == .read || risk == .keyedRead
}The compiler then enforces the allowlist exhaustively — a new risk variant that isn't listed here will not be remotely invokable by default.
| return true | ||
| } | ||
| guard command.allowsRemoteInvocation else { | ||
| ble.setDebugCommandStatus("\(command.title) blocked from external deep link") |
There was a problem hiding this comment.
Confirm this is the only deep-link enforcement site.
The gate correctly consults allowsRemoteInvocation here, but verify no other code path (e.g. payload-hex form, Shortcuts integration, or a second handleDeepLink branch) bypasses this guard. A single enforcement point is the right design; the risk is that a second entry point added later will not automatically inherit it.
Consider emitting an explicit audit log when a deep link is rejected so internal tooling can detect and reclassify affected commands:
ble.record(level: .warn, source: "ble.debug_command",
title: "deep_link.blocked",
body: "\(command.id) risk=\(command.risk) — allowsRemoteInvocation=false")This also helps diagnose broken Shortcuts automations without requiring a debugger.
- allowsRemoteInvocation computed property on GooseDebugCommandDefinition: only 'read' and 'keyed read' risk commands can be invoked via URL scheme - handleDebugCommandDeepLink validates command exists and allowsRemoteInvocation before executing; unknown or state-changing commands are blocked + logged - remoteURLExample returns 'Remote invocation disabled' for blocked commands Ported from b-nnett#15 (kobemartin)
Summary
Why
The custom URL scheme previously allowed an external app or webpage to immediately invoke a state-changing WHOOP research command while Goose was connected.
Validation
git diff --checkswiftc -parse GooseSwift/*.swift GooseWorkoutLiveActivityExtension/*.swift