fix(security): harden autoAllowSkills exec matching

This commit is contained in:
Peter Steinberger
2026-02-24 02:52:57 +00:00
parent e578521ef4
commit 90383e00e9
3 changed files with 71 additions and 4 deletions

View File

@@ -92,6 +92,10 @@ export function isSafeBinUsage(params: {
return validateSafeBinArgv(argv, profile);
}
function isPathScopedExecutableToken(token: string): boolean {
return token.includes("/") || token.includes("\\");
}
export type ExecAllowlistEvaluation = {
allowlistSatisfied: boolean;
allowlistMatches: ExecAllowlistEntry[];
@@ -147,10 +151,19 @@ function evaluateSegments(
platform: params.platform,
trustedSafeBinDirs: params.trustedSafeBinDirs,
});
const skillAllow =
allowSkills && segment.resolution?.executableName
? params.skillBins?.has(segment.resolution.executableName)
: false;
const rawExecutable = segment.resolution?.rawExecutable?.trim() ?? "";
const executableName = segment.resolution?.executableName;
const usesExplicitPath = isPathScopedExecutableToken(rawExecutable);
let skillAllow = false;
if (
allowSkills &&
segment.resolution?.resolvedPath &&
rawExecutable.length > 0 &&
!usesExplicitPath &&
executableName
) {
skillAllow = Boolean(params.skillBins?.has(executableName));
}
const by: ExecSegmentSatisfiedBy = match
? "allowlist"
: safe

View File

@@ -627,6 +627,59 @@ describe("exec approvals allowlist evaluation", () => {
});
expect(result.allowlistSatisfied).toBe(true);
});
it("does not satisfy auto-allow skills for explicit relative paths", () => {
const analysis = {
ok: true,
segments: [
{
raw: "./skill-bin",
argv: ["./skill-bin", "--help"],
resolution: {
rawExecutable: "./skill-bin",
resolvedPath: "/tmp/skill-bin",
executableName: "skill-bin",
},
},
],
};
const result = evaluateExecAllowlist({
analysis,
allowlist: [],
safeBins: new Set(),
skillBins: new Set(["skill-bin"]),
autoAllowSkills: true,
cwd: "/tmp",
});
expect(result.allowlistSatisfied).toBe(false);
expect(result.segmentSatisfiedBy).toEqual([null]);
});
it("does not satisfy auto-allow skills when command resolution is missing", () => {
const analysis = {
ok: true,
segments: [
{
raw: "skill-bin --help",
argv: ["skill-bin", "--help"],
resolution: {
rawExecutable: "skill-bin",
executableName: "skill-bin",
},
},
],
};
const result = evaluateExecAllowlist({
analysis,
allowlist: [],
safeBins: new Set(),
skillBins: new Set(["skill-bin"]),
autoAllowSkills: true,
cwd: "/tmp",
});
expect(result.allowlistSatisfied).toBe(false);
expect(result.segmentSatisfiedBy).toEqual([null]);
});
});
describe("exec approvals policy helpers", () => {