fix(exec): match bare * wildcard in allowlist entries (#25082)
The matchAllowlist() function skipped patterns without path separators (/, \, ~), causing a bare "*" wildcard entry to never reach the glob matcher. Since glob's single * maps to [^/]*, it would also fail against absolute paths. Handle bare "*" as a special case that matches any resolved executable path. Closes #25082
This commit is contained in:
committed by
Peter Steinberger
parent
e9216cb7dc
commit
0f0b2c0255
@@ -43,6 +43,22 @@ describe("exec approvals allowlist matching", () => {
|
||||
}
|
||||
});
|
||||
|
||||
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("requires a resolved path", () => {
|
||||
const match = matchAllowlist([{ pattern: "bin/rg" }], {
|
||||
rawExecutable: "bin/rg",
|
||||
@@ -543,6 +559,26 @@ describe("exec approvals shell allowlist (chained commands)", () => {
|
||||
expect(result.analysisOk).toBe(false);
|
||||
expect(result.allowlistSatisfied).toBe(false);
|
||||
});
|
||||
|
||||
it("satisfies allowlist when bare * wildcard is present", () => {
|
||||
const dir = makeTempDir();
|
||||
const binPath = path.join(dir, "mybin");
|
||||
fs.writeFileSync(binPath, "#!/bin/sh\n", { mode: 0o755 });
|
||||
const env = makePathEnv(dir);
|
||||
try {
|
||||
const result = evaluateShellAllowlist({
|
||||
command: "mybin --flag",
|
||||
allowlist: [{ pattern: "*" }],
|
||||
safeBins: new Set(),
|
||||
cwd: dir,
|
||||
env,
|
||||
});
|
||||
expect(result.analysisOk).toBe(true);
|
||||
expect(result.allowlistSatisfied).toBe(true);
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("exec approvals allowlist evaluation", () => {
|
||||
|
||||
@@ -223,7 +223,17 @@ export function matchAllowlist(
|
||||
entries: ExecAllowlistEntry[],
|
||||
resolution: CommandResolution | null,
|
||||
): ExecAllowlistEntry | null {
|
||||
if (!entries.length || !resolution?.resolvedPath) {
|
||||
if (!entries.length) {
|
||||
return null;
|
||||
}
|
||||
// A bare "*" wildcard allows any command regardless of resolution.
|
||||
// Check it before the resolvedPath guard so that unresolvable commands
|
||||
// (e.g. Windows executables without known extensions) still match.
|
||||
const bareWild = entries.find((e) => e.pattern?.trim() === "*");
|
||||
if (bareWild && resolution) {
|
||||
return bareWild;
|
||||
}
|
||||
if (!resolution?.resolvedPath) {
|
||||
return null;
|
||||
}
|
||||
const resolvedPath = resolution.resolvedPath;
|
||||
@@ -232,6 +242,12 @@ export function matchAllowlist(
|
||||
if (!pattern) {
|
||||
continue;
|
||||
}
|
||||
// A bare "*" wildcard means "allow any executable". Match immediately
|
||||
// without going through glob expansion (glob `*` maps to `[^/]*` which
|
||||
// would fail on absolute paths containing slashes).
|
||||
if (pattern === "*") {
|
||||
return entry;
|
||||
}
|
||||
const hasPath = pattern.includes("/") || pattern.includes("\\") || pattern.includes("~");
|
||||
if (!hasPath) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user