fix(macos): enforce path-only exec allowlist patterns

This commit is contained in:
Peter Steinberger
2026-02-21 22:58:18 +01:00
parent 2712883d16
commit dd41fadcaf
5 changed files with 81 additions and 14 deletions

View File

@@ -306,7 +306,7 @@ enum ExecApprovalsStore {
}
static func ensureFile() -> ExecApprovalsFile {
var file = self.loadFile()
var file = self.normalizeIncoming(self.loadFile())
if file.socket == nil { file.socket = ExecApprovalsSocketConfig(path: nil, token: nil) }
let path = file.socket?.path?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if path.isEmpty {
@@ -316,6 +316,18 @@ enum ExecApprovalsStore {
if token.isEmpty {
file.socket?.token = self.generateToken()
}
if var agents = file.agents {
for (key, entry) in agents {
guard let allowlist = entry.allowlist else { continue }
let migrated = allowlist.map { self.migrateLegacyPattern($0) }
if migrated != allowlist {
var next = entry
next.allowlist = migrated
agents[key] = next
}
}
file.agents = agents.isEmpty ? nil : agents
}
if file.agents == nil { file.agents = [:] }
self.saveFile(file)
return file
@@ -400,7 +412,7 @@ enum ExecApprovalsStore {
static func addAllowlistEntry(agentId: String?, pattern: String) {
let trimmed = pattern.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
guard !trimmed.isEmpty, self.isPathPattern(trimmed) else { return }
self.updateFile { file in
let key = self.agentKey(agentId)
var agents = file.agents ?? [:]
@@ -453,7 +465,7 @@ enum ExecApprovalsStore {
lastUsedCommand: item.lastUsedCommand,
lastResolvedPath: item.lastResolvedPath)
}
.filter { !$0.pattern.isEmpty }
.filter { !$0.pattern.isEmpty && self.isPathPattern($0.pattern) }
entry.allowlist = cleaned
agents[key] = entry
file.agents = agents
@@ -523,6 +535,37 @@ enum ExecApprovalsStore {
return trimmed.isEmpty ? nil : trimmed.lowercased()
}
private static func isPathPattern(_ pattern: String) -> Bool {
pattern.contains("/") || pattern.contains("~") || pattern.contains("\\")
}
private static func migrateLegacyPattern(_ entry: ExecAllowlistEntry) -> ExecAllowlistEntry {
let trimmedPattern = entry.pattern.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedResolved = entry.lastResolvedPath?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
guard !trimmedPattern.isEmpty else {
return ExecAllowlistEntry(
id: entry.id,
pattern: trimmedPattern,
lastUsedAt: entry.lastUsedAt,
lastUsedCommand: entry.lastUsedCommand,
lastResolvedPath: entry.lastResolvedPath)
}
if self.isPathPattern(trimmedPattern) || trimmedResolved.isEmpty || !self.isPathPattern(trimmedResolved) {
return ExecAllowlistEntry(
id: entry.id,
pattern: trimmedPattern,
lastUsedAt: entry.lastUsedAt,
lastUsedCommand: entry.lastUsedCommand,
lastResolvedPath: entry.lastResolvedPath)
}
return ExecAllowlistEntry(
id: entry.id,
pattern: trimmedResolved,
lastUsedAt: entry.lastUsedAt,
lastUsedCommand: entry.lastUsedCommand,
lastResolvedPath: entry.lastResolvedPath)
}
private static func mergeAgents(
current: ExecApprovalsAgent,
legacy: ExecApprovalsAgent) -> ExecApprovalsAgent