import fs from "node:fs"; import path from "node:path"; import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import { matchesExecAllowlistPattern } from "./exec-allowlist-pattern.js"; import type { ExecAllowlistEntry } from "./exec-approvals.types.js"; import { resolveExecWrapperTrustPlan } from "./exec-wrapper-trust-plan.js"; import {
resolveExecutablePath as resolveExecutableCandidatePath,
resolveExecutablePathCandidate,
} from "./executable-path.js";
// Strip trailing shell redirections (e.g. `2>&1`, `2>/dev/null`) so that // allow-always argPatterns built without them still match commands that include // them. LLMs commonly add or omit these between runs of the same cron job. const TRAILING_SHELL_REDIRECTIONS_RE = /\s+(?:[12]>&[12]|[12]>\/dev\/null)\s*$/;
function stripTrailingRedirections(value: string): string {
let prev = value; while (true) { const next = prev.replace(TRAILING_SHELL_REDIRECTIONS_RE, ""); if (next === prev) { return next;
}
prev = next;
}
}
function matchArgPattern(argPattern: string, argv: string[], platform?: string | null): boolean { // Patterns built by buildArgPatternFromArgv use \x00 as the argument separator and // always include a trailing \x00 sentinel so that every auto-generated pattern // (including zero-arg "^\x00\x00$" and single-arg "^hello world\x00$") contains at // least one \x00. This lets matchArgPattern detect the join style unambiguously // via .includes("\x00") without misidentifying anchored hand-authored patterns. // Legacy hand-authored patterns use a plain space and contain no \x00. // When \x00 style is active, a trailing \x00 is appended to the joined args string // to match the sentinel embedded in the pattern. // // Zero args use a double sentinel "\x00\x00" to distinguish [] from [""] — both // join to "" but must match different patterns ("^\x00\x00$" vs "^\x00$"). const sep = argPattern.includes("\x00") ? "\x00" : " "; const argsSlice = argv.slice(1); const argsString =
sep === "\x00"
? argsSlice.length === 0
? "\x00\x00"// zero args: double sentinel matches "^\x00\x00$" pattern
: argsSlice.join(sep) + sep // trailing sentinel to match pattern format
: argsSlice.join(sep); try { const regex = new RegExp(argPattern); if (regex.test(argsString)) { returntrue;
} // On Windows, LLMs may use forward slashes (`C:/path`) or backslashes // (`C:\path`) interchangeably. Normalize to backslashes and retry so // that an argPattern built from one style still matches the other. // Use the caller-supplied target platform so Linux gateways evaluating // Windows node commands also perform the normalization. const effectivePlatform = normalizeLowercaseStringOrEmpty(platform ?? process.platform); if (effectivePlatform.startsWith("win")) { const normalized = argsString.replace(/\//g, "\\"); if (normalized !== argsString && regex.test(normalized)) { returntrue;
}
} // Retry after stripping trailing shell redirections (2>&1, etc.) so that // patterns saved without them still match commands that include them. // Only applies for space-joined (legacy hand-authored) patterns. For // \x00-joined auto-generated patterns, redirections are already blocked // upstream by findWindowsUnsupportedToken, so any surviving 2>&1 token // is a literal data argument and must not be stripped. if (sep === " ") { const stripped = stripTrailingRedirections(argsString); if (stripped !== argsString && regex.test(stripped)) { returntrue;
}
} returnfalse;
} catch { returnfalse;
}
}
function matchesExecutableBasenamePattern(
pattern: string,
resolution: ExecutableResolution,
): boolean { // Bare command-name allowlist entries are for PATH-resolved commands. A raw // path such as ./rg or /tmp/rg must use a path allowlist entry so a workspace // binary cannot inherit trust from a global command-name entry. if (hasPathSelector(resolution.rawExecutable)) { returnfalse;
} const candidates = new Set<string>(); if (resolution.executableName) {
candidates.add(resolution.executableName);
} if (resolution.resolvedPath) {
candidates.add(path.basename(resolution.resolvedPath));
} return [...candidates].some((candidate) => matchesExecAllowlistPattern(pattern, candidate));
}
export function matchAllowlist(
entries: ExecAllowlistEntry[],
resolution: ExecutableResolution | null,
argv?: string[],
platform?: string | null,
): ExecAllowlistEntry | null { if (!entries.length) { returnnull;
} // A bare "*" wildcard allows any parsed executable command. // Check it before the resolvedPath guard so unresolved PATH lookups still // match (for example platform-specific executables without known extensions). const bareWild = entries.find((e) => e.pattern?.trim() === "*" && !e.argPattern); if (bareWild && resolution) { return bareWild;
} if (!resolution?.resolvedPath) { returnnull;
} const resolvedPath = resolution.resolvedPath; // argPattern matching is currently Windows-only. On other platforms every // path-matched entry is treated as a match regardless of argPattern, which // preserves the pre-existing behaviour. // Use the caller-supplied target platform rather than process.platform so that // a Linux gateway evaluating a Windows node command applies argPattern correctly. const effectivePlatform = platform ?? process.platform; const useArgPattern = normalizeLowercaseStringOrEmpty(effectivePlatform).startsWith("win");
let pathOnlyMatch: ExecAllowlistEntry | null = null; for (const entry of entries) { const pattern = entry.pattern?.trim(); if (!pattern) { continue;
} const patternMatches = hasPathSelector(pattern)
? matchesExecAllowlistPattern(pattern, resolvedPath)
: pattern !== "*" && matchesExecutableBasenamePattern(pattern, resolution); if (!patternMatches) { continue;
} if (!useArgPattern) { // Non-Windows: first path match wins (legacy behaviour). return entry;
} if (!entry.argPattern) { if (!pathOnlyMatch) {
pathOnlyMatch = entry;
} continue;
} // Entry has argPattern — check argv match. if (argv && matchArgPattern(entry.argPattern, argv, platform)) { return entry;
}
} return pathOnlyMatch;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.