diff --git a/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift b/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift index eb4fa6818..7d8189248 100644 --- a/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift +++ b/apps/macos/Sources/ClawdbotProtocol/GatewayModels.swift @@ -1908,6 +1908,7 @@ public struct ExecApprovalsSnapshot: Codable, Sendable { } public struct ExecApprovalRequestParams: Codable, Sendable { + public let id: String? public let command: String public let cwd: String? public let host: String? @@ -1919,6 +1920,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable { public let timeoutms: Int? public init( + id: String?, command: String, cwd: String?, host: String?, @@ -1929,6 +1931,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable { sessionkey: String?, timeoutms: Int? ) { + self.id = id self.command = command self.cwd = cwd self.host = host @@ -1940,6 +1943,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable { self.timeoutms = timeoutms } private enum CodingKeys: String, CodingKey { + case id case command case cwd case host diff --git a/apps/shared/ClawdbotKit/Sources/ClawdbotProtocol/GatewayModels.swift b/apps/shared/ClawdbotKit/Sources/ClawdbotProtocol/GatewayModels.swift index eb4fa6818..7d8189248 100644 --- a/apps/shared/ClawdbotKit/Sources/ClawdbotProtocol/GatewayModels.swift +++ b/apps/shared/ClawdbotKit/Sources/ClawdbotProtocol/GatewayModels.swift @@ -1908,6 +1908,7 @@ public struct ExecApprovalsSnapshot: Codable, Sendable { } public struct ExecApprovalRequestParams: Codable, Sendable { + public let id: String? public let command: String public let cwd: String? public let host: String? @@ -1919,6 +1920,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable { public let timeoutms: Int? public init( + id: String?, command: String, cwd: String?, host: String?, @@ -1929,6 +1931,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable { sessionkey: String?, timeoutms: Int? ) { + self.id = id self.command = command self.cwd = cwd self.host = host @@ -1940,6 +1943,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable { self.timeoutms = timeoutms } private enum CodingKeys: String, CodingKey { + case id case command case cwd case host diff --git a/src/infra/exec-approvals.test.ts b/src/infra/exec-approvals.test.ts index e0ff6a33b..cbafad759 100644 --- a/src/infra/exec-approvals.test.ts +++ b/src/infra/exec-approvals.test.ts @@ -72,12 +72,13 @@ describe("exec approvals command resolution", () => { const dir = makeTempDir(); const binDir = path.join(dir, "bin"); fs.mkdirSync(binDir, { recursive: true }); - const exe = path.join(binDir, "rg"); + const exeName = process.platform === "win32" ? "rg.exe" : "rg"; + const exe = path.join(binDir, exeName); fs.writeFileSync(exe, ""); fs.chmodSync(exe, 0o755); const res = resolveCommandResolution("rg -n foo", undefined, { PATH: binDir }); expect(res?.resolvedPath).toBe(exe); - expect(res?.executableName).toBe("rg"); + expect(res?.executableName).toBe(exeName); }); it("resolves relative paths against cwd", () => { @@ -127,7 +128,8 @@ describe("exec approvals safe bins", () => { const dir = makeTempDir(); const binDir = path.join(dir, "bin"); fs.mkdirSync(binDir, { recursive: true }); - const exe = path.join(binDir, "jq"); + const exeName = process.platform === "win32" ? "jq.exe" : "jq"; + const exe = path.join(binDir, exeName); fs.writeFileSync(exe, ""); fs.chmodSync(exe, 0o755); const res = analyzeShellCommand({ @@ -150,7 +152,8 @@ describe("exec approvals safe bins", () => { const dir = makeTempDir(); const binDir = path.join(dir, "bin"); fs.mkdirSync(binDir, { recursive: true }); - const exe = path.join(binDir, "jq"); + const exeName = process.platform === "win32" ? "jq.exe" : "jq"; + const exe = path.join(binDir, exeName); fs.writeFileSync(exe, ""); fs.chmodSync(exe, 0o755); const file = path.join(dir, "secret.json"); diff --git a/src/infra/exec-approvals.ts b/src/infra/exec-approvals.ts index 06d58b0d5..ee8b1c541 100644 --- a/src/infra/exec-approvals.ts +++ b/src/infra/exec-approvals.ts @@ -660,7 +660,11 @@ export function isSafeBinUsage(params: { if (params.safeBins.size === 0) return false; const resolution = params.resolution; const execName = resolution?.executableName?.toLowerCase(); - if (!execName || !params.safeBins.has(execName)) return false; + if (!execName) return false; + const matchesSafeBin = + params.safeBins.has(execName) || + (process.platform === "win32" && params.safeBins.has(path.parse(execName).name)); + if (!matchesSafeBin) return false; if (!resolution?.resolvedPath) return false; const cwd = params.cwd ?? process.cwd(); const exists = params.fileExists ?? defaultFileExists;