diff --git a/src/gateway/server-methods/exec-approvals.ts b/src/gateway/server-methods/exec-approvals.ts index df01574599..cd61c0c5ff 100644 --- a/src/gateway/server-methods/exec-approvals.ts +++ b/src/gateway/server-methods/exec-approvals.ts @@ -1,9 +1,9 @@ import type { GatewayRequestHandlers, RespondFn } from "./types.js"; import { ensureExecApprovals, + mergeExecApprovalsSocketDefaults, normalizeExecApprovals, readExecApprovalsSnapshot, - resolveExecApprovalsSocketPath, saveExecApprovals, type ExecApprovalsFile, type ExecApprovalsSnapshot, @@ -134,18 +134,7 @@ export const execApprovalsHandlers: GatewayRequestHandlers = { return; } const normalized = normalizeExecApprovals(incoming as ExecApprovalsFile); - const currentSocketPath = snapshot.file.socket?.path?.trim(); - const currentToken = snapshot.file.socket?.token?.trim(); - const socketPath = - normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath(); - const token = normalized.socket?.token?.trim() ?? currentToken ?? ""; - const next: ExecApprovalsFile = { - ...normalized, - socket: { - path: socketPath, - token, - }, - }; + const next = mergeExecApprovalsSocketDefaults({ normalized, current: snapshot.file }); saveExecApprovals(next); const nextSnapshot = readExecApprovalsSnapshot(); respond( diff --git a/src/infra/exec-approvals.socket-defaults.test.ts b/src/infra/exec-approvals.socket-defaults.test.ts new file mode 100644 index 0000000000..b1f33ea984 --- /dev/null +++ b/src/infra/exec-approvals.socket-defaults.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { mergeExecApprovalsSocketDefaults, normalizeExecApprovals } from "./exec-approvals.js"; + +describe("mergeExecApprovalsSocketDefaults", () => { + it("prefers normalized socket, then current, then default path", () => { + const normalized = normalizeExecApprovals({ + version: 1, + agents: {}, + socket: { path: "/tmp/a.sock", token: "a" }, + }); + const current = normalizeExecApprovals({ + version: 1, + agents: {}, + socket: { path: "/tmp/b.sock", token: "b" }, + }); + const merged = mergeExecApprovalsSocketDefaults({ normalized, current }); + expect(merged.socket?.path).toBe("/tmp/a.sock"); + expect(merged.socket?.token).toBe("a"); + }); + + it("falls back to current token when missing in normalized", () => { + const normalized = normalizeExecApprovals({ version: 1, agents: {} }); + const current = normalizeExecApprovals({ + version: 1, + agents: {}, + socket: { path: "/tmp/b.sock", token: "b" }, + }); + const merged = mergeExecApprovalsSocketDefaults({ normalized, current }); + expect(merged.socket?.path).toBeTruthy(); + expect(merged.socket?.token).toBe("b"); + }); +}); diff --git a/src/infra/exec-approvals.ts b/src/infra/exec-approvals.ts index 6423d93599..56daa99e58 100644 --- a/src/infra/exec-approvals.ts +++ b/src/infra/exec-approvals.ts @@ -241,6 +241,24 @@ export function normalizeExecApprovals(file: ExecApprovalsFile): ExecApprovalsFi return normalized; } +export function mergeExecApprovalsSocketDefaults(params: { + normalized: ExecApprovalsFile; + current?: ExecApprovalsFile; +}): ExecApprovalsFile { + const currentSocketPath = params.current?.socket?.path?.trim(); + const currentToken = params.current?.socket?.token?.trim(); + const socketPath = + params.normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath(); + const token = params.normalized.socket?.token?.trim() ?? currentToken ?? ""; + return { + ...params.normalized, + socket: { + path: socketPath, + token, + }, + }; +} + function generateToken(): string { return crypto.randomBytes(24).toString("base64url"); } diff --git a/src/node-host/invoke.ts b/src/node-host/invoke.ts index a610772fb9..b0616e23b1 100644 --- a/src/node-host/invoke.ts +++ b/src/node-host/invoke.ts @@ -12,12 +12,12 @@ import { evaluateShellAllowlist, requiresExecApproval, normalizeExecApprovals, + mergeExecApprovalsSocketDefaults, recordAllowlistUse, resolveExecApprovals, resolveSafeBins, ensureExecApprovals, readExecApprovalsSnapshot, - resolveExecApprovalsSocketPath, saveExecApprovals, type ExecAsk, type ExecApprovalsFile, @@ -422,18 +422,7 @@ export async function handleInvoke( const snapshot = readExecApprovalsSnapshot(); requireExecApprovalsBaseHash(params, snapshot); const normalized = normalizeExecApprovals(params.file); - const currentSocketPath = snapshot.file.socket?.path?.trim(); - const currentToken = snapshot.file.socket?.token?.trim(); - const socketPath = - normalized.socket?.path?.trim() ?? currentSocketPath ?? resolveExecApprovalsSocketPath(); - const token = normalized.socket?.token?.trim() ?? currentToken ?? ""; - const next: ExecApprovalsFile = { - ...normalized, - socket: { - path: socketPath, - token, - }, - }; + const next = mergeExecApprovalsSocketDefaults({ normalized, current: snapshot.file }); saveExecApprovals(next); const nextSnapshot = readExecApprovalsSnapshot(); const payload: ExecApprovalsSnapshot = {