diff --git a/src/infra/exec-approvals.test.ts b/src/infra/exec-approvals.test.ts index 57abeb695..4b47a80d8 100644 --- a/src/infra/exec-approvals.test.ts +++ b/src/infra/exec-approvals.test.ts @@ -33,7 +33,7 @@ function makeTempDir() { } describe("exec approvals allowlist matching", () => { - it("ignores basename-only patterns", () => { + it("matches basename-only patterns case-insensitively", () => { const resolution = { rawExecutable: "rg", resolvedPath: "/opt/homebrew/bin/rg", @@ -41,7 +41,7 @@ describe("exec approvals allowlist matching", () => { }; const entries: ExecAllowlistEntry[] = [{ pattern: "RG" }]; const match = matchAllowlist(entries, resolution); - expect(match).toBeNull(); + expect(match?.pattern).toBe("RG"); }); it("matches by resolved path with **", () => { @@ -66,7 +66,7 @@ describe("exec approvals allowlist matching", () => { expect(match).toBeNull(); }); - it("requires a resolved path", () => { + it("falls back to rawExecutable when resolvedPath is undefined", () => { const resolution = { rawExecutable: "bin/rg", resolvedPath: undefined, @@ -74,7 +74,7 @@ describe("exec approvals allowlist matching", () => { }; const entries: ExecAllowlistEntry[] = [{ pattern: "bin/rg" }]; const match = matchAllowlist(entries, resolution); - expect(match).toBeNull(); + expect(match?.pattern).toBe("bin/rg"); }); }); diff --git a/src/infra/exec-approvals.ts b/src/infra/exec-approvals.ts index 0830ed89a..c2d06fd99 100644 --- a/src/infra/exec-approvals.ts +++ b/src/infra/exec-approvals.ts @@ -508,14 +508,20 @@ export function matchAllowlist( entries: ExecAllowlistEntry[], resolution: CommandResolution | null, ): ExecAllowlistEntry | null { - if (!entries.length || !resolution?.resolvedPath) return null; + if (!entries.length || !resolution) return null; + const rawExecutable = resolution.rawExecutable; const resolvedPath = resolution.resolvedPath; + const executableName = resolution.executableName; for (const entry of entries) { const pattern = entry.pattern?.trim(); if (!pattern) continue; const hasPath = pattern.includes("/") || pattern.includes("\\") || pattern.includes("~"); - if (!hasPath) continue; - if (matchesPattern(pattern, resolvedPath)) return entry; + if (hasPath) { + const target = resolvedPath ?? rawExecutable; + if (target && matchesPattern(pattern, target)) return entry; + continue; + } + if (executableName && matchesPattern(pattern, executableName)) return entry; } return null; }