refactor(exec-approvals): share socket default merge

This commit is contained in:
Peter Steinberger
2026-02-15 17:36:08 +00:00
parent 5c88d3c9f1
commit a0e763168f
4 changed files with 54 additions and 26 deletions

View File

@@ -1,9 +1,9 @@
import type { GatewayRequestHandlers, RespondFn } from "./types.js"; import type { GatewayRequestHandlers, RespondFn } from "./types.js";
import { import {
ensureExecApprovals, ensureExecApprovals,
mergeExecApprovalsSocketDefaults,
normalizeExecApprovals, normalizeExecApprovals,
readExecApprovalsSnapshot, readExecApprovalsSnapshot,
resolveExecApprovalsSocketPath,
saveExecApprovals, saveExecApprovals,
type ExecApprovalsFile, type ExecApprovalsFile,
type ExecApprovalsSnapshot, type ExecApprovalsSnapshot,
@@ -134,18 +134,7 @@ export const execApprovalsHandlers: GatewayRequestHandlers = {
return; return;
} }
const normalized = normalizeExecApprovals(incoming as ExecApprovalsFile); const normalized = normalizeExecApprovals(incoming as ExecApprovalsFile);
const currentSocketPath = snapshot.file.socket?.path?.trim(); const next = mergeExecApprovalsSocketDefaults({ normalized, current: snapshot.file });
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,
},
};
saveExecApprovals(next); saveExecApprovals(next);
const nextSnapshot = readExecApprovalsSnapshot(); const nextSnapshot = readExecApprovalsSnapshot();
respond( respond(

View File

@@ -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");
});
});

View File

@@ -241,6 +241,24 @@ export function normalizeExecApprovals(file: ExecApprovalsFile): ExecApprovalsFi
return normalized; 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 { function generateToken(): string {
return crypto.randomBytes(24).toString("base64url"); return crypto.randomBytes(24).toString("base64url");
} }

View File

@@ -12,12 +12,12 @@ import {
evaluateShellAllowlist, evaluateShellAllowlist,
requiresExecApproval, requiresExecApproval,
normalizeExecApprovals, normalizeExecApprovals,
mergeExecApprovalsSocketDefaults,
recordAllowlistUse, recordAllowlistUse,
resolveExecApprovals, resolveExecApprovals,
resolveSafeBins, resolveSafeBins,
ensureExecApprovals, ensureExecApprovals,
readExecApprovalsSnapshot, readExecApprovalsSnapshot,
resolveExecApprovalsSocketPath,
saveExecApprovals, saveExecApprovals,
type ExecAsk, type ExecAsk,
type ExecApprovalsFile, type ExecApprovalsFile,
@@ -422,18 +422,7 @@ export async function handleInvoke(
const snapshot = readExecApprovalsSnapshot(); const snapshot = readExecApprovalsSnapshot();
requireExecApprovalsBaseHash(params, snapshot); requireExecApprovalsBaseHash(params, snapshot);
const normalized = normalizeExecApprovals(params.file); const normalized = normalizeExecApprovals(params.file);
const currentSocketPath = snapshot.file.socket?.path?.trim(); const next = mergeExecApprovalsSocketDefaults({ normalized, current: snapshot.file });
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,
},
};
saveExecApprovals(next); saveExecApprovals(next);
const nextSnapshot = readExecApprovalsSnapshot(); const nextSnapshot = readExecApprovalsSnapshot();
const payload: ExecApprovalsSnapshot = { const payload: ExecApprovalsSnapshot = {