refactor(security): extract safeBins trust resolver

This commit is contained in:
Peter Steinberger
2026-02-18 05:01:25 +01:00
parent e8154c12e6
commit ac0db68235
5 changed files with 221 additions and 81 deletions

View File

@@ -12,48 +12,7 @@ import {
type CommandResolution,
type ExecCommandSegment,
} from "./exec-approvals-analysis.js";
const DEFAULT_SAFE_BIN_TRUSTED_DIRS = [
"/bin",
"/usr/bin",
"/usr/local/bin",
"/opt/homebrew/bin",
"/opt/local/bin",
"/snap/bin",
"/run/current-system/sw/bin",
];
function normalizeTrustedDir(value: string): string | null {
const trimmed = value.trim();
if (!trimmed) {
return null;
}
return path.resolve(trimmed);
}
function collectTrustedSafeBinDirs(): Set<string> {
const trusted = new Set<string>();
for (const entry of DEFAULT_SAFE_BIN_TRUSTED_DIRS) {
const normalized = normalizeTrustedDir(entry);
if (normalized) {
trusted.add(normalized);
}
}
const pathEntries = (process.env.PATH ?? process.env.Path ?? "")
.split(path.delimiter)
.map((entry) => normalizeTrustedDir(entry))
.filter((entry): entry is string => Boolean(entry));
for (const entry of pathEntries) {
trusted.add(entry);
}
return trusted;
}
const TRUSTED_SAFE_BIN_DIRS = collectTrustedSafeBinDirs();
function isTrustedSafeBinPath(resolvedPath: string): boolean {
return TRUSTED_SAFE_BIN_DIRS.has(path.dirname(path.resolve(resolvedPath)));
}
import { isTrustedSafeBinPath } from "./exec-safe-bin-trust.js";
function isPathLikeToken(value: string): boolean {
const trimmed = value.trim();
@@ -109,6 +68,7 @@ export function isSafeBinUsage(params: {
safeBins: Set<string>;
cwd?: string;
fileExists?: (filePath: string) => boolean;
trustedSafeBinDirs?: ReadonlySet<string>;
}): boolean {
// Windows host exec uses PowerShell, which has different parsing/expansion rules.
// Keep safeBins conservative there (require explicit allowlist entries).
@@ -132,7 +92,12 @@ export function isSafeBinUsage(params: {
if (!resolution?.resolvedPath) {
return false;
}
if (!isTrustedSafeBinPath(resolution.resolvedPath)) {
if (
!isTrustedSafeBinPath({
resolvedPath: resolution.resolvedPath,
trustedDirs: params.trustedSafeBinDirs,
})
) {
return false;
}
const cwd = params.cwd ?? process.cwd();