test(exec): add regressions for safe-bin metadata and chain semantics

This commit is contained in:
Peter Steinberger
2026-02-24 03:10:05 +00:00
parent a67689a7e3
commit 64aab80201
2 changed files with 99 additions and 0 deletions

View File

@@ -680,6 +680,71 @@ describe("exec approvals allowlist evaluation", () => {
expect(result.allowlistSatisfied).toBe(false);
expect(result.segmentSatisfiedBy).toEqual([null]);
});
it("returns empty segment details for chain misses", () => {
const segment = {
raw: "tool",
argv: ["tool"],
resolution: {
rawExecutable: "tool",
resolvedPath: "/usr/bin/tool",
executableName: "tool",
},
};
const analysis = {
ok: true,
segments: [segment],
chains: [[segment]],
};
const result = evaluateExecAllowlist({
analysis,
allowlist: [{ pattern: "/usr/bin/other" }],
safeBins: new Set(),
cwd: "/tmp",
});
expect(result.allowlistSatisfied).toBe(false);
expect(result.allowlistMatches).toEqual([]);
expect(result.segmentSatisfiedBy).toEqual([]);
});
it("aggregates segment satisfaction across chains", () => {
const allowlistSegment = {
raw: "tool",
argv: ["tool"],
resolution: {
rawExecutable: "tool",
resolvedPath: "/usr/bin/tool",
executableName: "tool",
},
};
const safeBinSegment = {
raw: "jq .foo",
argv: ["jq", ".foo"],
resolution: {
rawExecutable: "jq",
resolvedPath: "/usr/bin/jq",
executableName: "jq",
},
};
const analysis = {
ok: true,
segments: [allowlistSegment, safeBinSegment],
chains: [[allowlistSegment], [safeBinSegment]],
};
const result = evaluateExecAllowlist({
analysis,
allowlist: [{ pattern: "/usr/bin/tool" }],
safeBins: normalizeSafeBins(["jq"]),
cwd: "/tmp",
});
if (process.platform === "win32") {
expect(result.allowlistSatisfied).toBe(false);
return;
}
expect(result.allowlistSatisfied).toBe(true);
expect(result.allowlistMatches.map((entry) => entry.pattern)).toEqual(["/usr/bin/tool"]);
expect(result.segmentSatisfiedBy).toEqual(["allowlist", "safeBins"]);
});
});
describe("exec approvals policy helpers", () => {

View File

@@ -4,6 +4,8 @@ import { describe, expect, it } from "vitest";
import {
SAFE_BIN_PROFILE_FIXTURES,
SAFE_BIN_PROFILES,
buildLongFlagPrefixMap,
collectKnownLongFlags,
renderSafeBinDeniedFlagsDocBullets,
validateSafeBinArgv,
} from "./exec-safe-bin-policy.js";
@@ -76,6 +78,38 @@ describe("exec safe bin policy wc", () => {
});
});
describe("exec safe bin policy long-option metadata", () => {
it("precomputes long-option prefix mappings for compiled profiles", () => {
const sortProfile = SAFE_BIN_PROFILES.sort;
expect(sortProfile.knownLongFlagsSet?.has("--compress-program")).toBe(true);
expect(sortProfile.longFlagPrefixMap?.get("--compress-prog")).toBe("--compress-program");
expect(sortProfile.longFlagPrefixMap?.get("--f")).toBe(null);
});
it("preserves behavior when profile metadata is missing and rebuilt at runtime", () => {
const sortProfile = SAFE_BIN_PROFILES.sort;
const withoutMetadata = {
...sortProfile,
knownLongFlags: undefined,
knownLongFlagsSet: undefined,
longFlagPrefixMap: undefined,
};
expect(validateSafeBinArgv(["--compress-prog=sh"], withoutMetadata)).toBe(false);
expect(validateSafeBinArgv(["--totally-unknown=1"], withoutMetadata)).toBe(false);
});
it("builds prefix maps from collected long flags", () => {
const sortProfile = SAFE_BIN_PROFILES.sort;
const flags = collectKnownLongFlags(
sortProfile.allowedValueFlags ?? new Set(),
sortProfile.deniedFlags ?? new Set(),
);
const prefixMap = buildLongFlagPrefixMap(flags);
expect(prefixMap.get("--compress-pr")).toBe("--compress-program");
expect(prefixMap.get("--f")).toBe(null);
});
});
describe("exec safe bin policy denied-flag matrix", () => {
for (const [binName, fixture] of Object.entries(SAFE_BIN_PROFILE_FIXTURES)) {
const profile = SAFE_BIN_PROFILES[binName];