test: extract exec allowlist matching coverage

This commit is contained in:
Peter Steinberger 2026-03-13 20:34:25 +00:00
parent 3957f29e2f
commit f568bd23d8
2 changed files with 60 additions and 69 deletions

View File

@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";
import { matchAllowlist, type ExecAllowlistEntry } from "./exec-approvals.js";
describe("exec allowlist matching", () => {
const baseResolution = {
rawExecutable: "rg",
resolvedPath: "/opt/homebrew/bin/rg",
executableName: "rg",
};
it("handles wildcard and path matching semantics", () => {
const cases: Array<{ entries: ExecAllowlistEntry[]; expectedPattern: string | null }> = [
{ entries: [{ pattern: "RG" }], expectedPattern: null },
{ entries: [{ pattern: "/opt/**/rg" }], expectedPattern: "/opt/**/rg" },
{ entries: [{ pattern: "/opt/*/rg" }], expectedPattern: null },
];
for (const testCase of cases) {
const match = matchAllowlist(testCase.entries, baseResolution);
expect(match?.pattern ?? null).toBe(testCase.expectedPattern);
}
});
it("matches bare wildcard patterns against arbitrary resolved executables", () => {
expect(matchAllowlist([{ pattern: "*" }], baseResolution)?.pattern).toBe("*");
expect(
matchAllowlist([{ pattern: "*" }], {
rawExecutable: "python3",
resolvedPath: "/usr/bin/python3",
executableName: "python3",
})?.pattern,
).toBe("*");
});
it("matches absolute paths containing regex metacharacters literally", () => {
const plusPathCases = ["/usr/bin/g++", "/usr/bin/clang++"];
for (const candidatePath of plusPathCases) {
const match = matchAllowlist([{ pattern: candidatePath }], {
rawExecutable: candidatePath,
resolvedPath: candidatePath,
executableName: candidatePath.split("/").at(-1) ?? candidatePath,
});
expect(match?.pattern).toBe(candidatePath);
}
expect(
matchAllowlist([{ pattern: "/usr/bin/*++" }], {
rawExecutable: "/usr/bin/g++",
resolvedPath: "/usr/bin/g++",
executableName: "g++",
})?.pattern,
).toBe("/usr/bin/*++");
expect(
matchAllowlist([{ pattern: "/opt/builds/tool[1](stable)" }], {
rawExecutable: "/opt/builds/tool[1](stable)",
resolvedPath: "/opt/builds/tool[1](stable)",
executableName: "tool[1](stable)",
})?.pattern,
).toBe("/opt/builds/tool[1](stable)");
});
});

View File

@ -9,7 +9,6 @@ import {
buildSafeBinsShellCommand, buildSafeBinsShellCommand,
evaluateExecAllowlist, evaluateExecAllowlist,
evaluateShellAllowlist, evaluateShellAllowlist,
matchAllowlist,
maxAsk, maxAsk,
mergeExecApprovalsSocketDefaults, mergeExecApprovalsSocketDefaults,
minSecurity, minSecurity,
@ -18,76 +17,8 @@ import {
requiresExecApproval, requiresExecApproval,
resolveExecApprovalsPath, resolveExecApprovalsPath,
resolveExecApprovalsSocketPath, resolveExecApprovalsSocketPath,
type ExecAllowlistEntry,
} from "./exec-approvals.js"; } from "./exec-approvals.js";
describe("exec approvals allowlist matching", () => {
const baseResolution = {
rawExecutable: "rg",
resolvedPath: "/opt/homebrew/bin/rg",
executableName: "rg",
};
it("handles wildcard/path matching semantics", () => {
const cases: Array<{ entries: ExecAllowlistEntry[]; expectedPattern: string | null }> = [
{ entries: [{ pattern: "RG" }], expectedPattern: null },
{ entries: [{ pattern: "/opt/**/rg" }], expectedPattern: "/opt/**/rg" },
{ entries: [{ pattern: "/opt/*/rg" }], expectedPattern: null },
];
for (const testCase of cases) {
const match = matchAllowlist(testCase.entries, baseResolution);
expect(match?.pattern ?? null).toBe(testCase.expectedPattern);
}
});
it("matches bare * wildcard pattern against any resolved path", () => {
const match = matchAllowlist([{ pattern: "*" }], baseResolution);
expect(match).not.toBeNull();
expect(match?.pattern).toBe("*");
});
it("matches bare * wildcard against arbitrary executables", () => {
const match = matchAllowlist([{ pattern: "*" }], {
rawExecutable: "python3",
resolvedPath: "/usr/bin/python3",
executableName: "python3",
});
expect(match).not.toBeNull();
expect(match?.pattern).toBe("*");
});
it("matches absolute paths containing regex metacharacters", () => {
const plusPathCases = ["/usr/bin/g++", "/usr/bin/clang++"];
for (const candidatePath of plusPathCases) {
const match = matchAllowlist([{ pattern: candidatePath }], {
rawExecutable: candidatePath,
resolvedPath: candidatePath,
executableName: candidatePath.split("/").at(-1) ?? candidatePath,
});
expect(match?.pattern).toBe(candidatePath);
}
});
it("does not throw when wildcard globs are mixed with + in path", () => {
const match = matchAllowlist([{ pattern: "/usr/bin/*++" }], {
rawExecutable: "/usr/bin/g++",
resolvedPath: "/usr/bin/g++",
executableName: "g++",
});
expect(match?.pattern).toBe("/usr/bin/*++");
});
it("matches paths containing []() regex tokens literally", () => {
const literalPattern = "/opt/builds/tool[1](stable)";
const match = matchAllowlist([{ pattern: literalPattern }], {
rawExecutable: literalPattern,
resolvedPath: literalPattern,
executableName: "tool[1](stable)",
});
expect(match?.pattern).toBe(literalPattern);
});
});
describe("mergeExecApprovalsSocketDefaults", () => { describe("mergeExecApprovalsSocketDefaults", () => {
it("prefers normalized socket, then current, then default path", () => { it("prefers normalized socket, then current, then default path", () => {
const normalized = normalizeExecApprovals({ const normalized = normalizeExecApprovals({