Merge branch 'main' into feat/mattermost-channel
This commit is contained in:
@@ -358,14 +358,20 @@ function resolveExecutablePath(rawExecutable: string, cwd?: string, env?: NodeJS
|
||||
const candidate = path.resolve(base, expanded);
|
||||
return isExecutableFile(candidate) ? candidate : undefined;
|
||||
}
|
||||
const envPath = env?.PATH ?? process.env.PATH ?? "";
|
||||
const envPath = env?.PATH ?? env?.Path ?? process.env.PATH ?? process.env.Path ?? "";
|
||||
const entries = envPath.split(path.delimiter).filter(Boolean);
|
||||
const hasExtension = process.platform === "win32" && path.extname(expanded).length > 0;
|
||||
const extensions =
|
||||
process.platform === "win32"
|
||||
? hasExtension
|
||||
? [""]
|
||||
: (env?.PATHEXT ?? process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM")
|
||||
: (
|
||||
env?.PATHEXT ??
|
||||
env?.Pathext ??
|
||||
process.env.PATHEXT ??
|
||||
process.env.Pathext ??
|
||||
".EXE;.CMD;.BAT;.COM"
|
||||
)
|
||||
.split(";")
|
||||
.map((ext) => ext.toLowerCase())
|
||||
: [""];
|
||||
@@ -459,6 +465,21 @@ function matchesPattern(pattern: string, target: string): boolean {
|
||||
return regex.test(normalizedTarget);
|
||||
}
|
||||
|
||||
function resolveAllowlistCandidatePath(
|
||||
resolution: CommandResolution | null,
|
||||
cwd?: string,
|
||||
): string | undefined {
|
||||
if (!resolution) return undefined;
|
||||
if (resolution.resolvedPath) return resolution.resolvedPath;
|
||||
const raw = resolution.rawExecutable?.trim();
|
||||
if (!raw) return undefined;
|
||||
const expanded = raw.startsWith("~") ? expandHome(raw) : raw;
|
||||
if (!expanded.includes("/") && !expanded.includes("\\")) return undefined;
|
||||
if (path.isAbsolute(expanded)) return expanded;
|
||||
const base = cwd && cwd.trim() ? cwd.trim() : process.cwd();
|
||||
return path.resolve(base, expanded);
|
||||
}
|
||||
|
||||
export function matchAllowlist(
|
||||
entries: ExecAllowlistEntry[],
|
||||
resolution: CommandResolution | null,
|
||||
@@ -513,7 +534,7 @@ function splitShellPipeline(command: string): { ok: boolean; reason?: string; se
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (!inSingle && ch === "\\") {
|
||||
if (!inSingle && !inDouble && ch === "\\") {
|
||||
escaped = true;
|
||||
buf += ch;
|
||||
continue;
|
||||
@@ -589,7 +610,7 @@ function tokenizeShellSegment(segment: string): string[] | null {
|
||||
escaped = false;
|
||||
continue;
|
||||
}
|
||||
if (!inSingle && ch === "\\") {
|
||||
if (!inSingle && !inDouble && ch === "\\") {
|
||||
escaped = true;
|
||||
continue;
|
||||
}
|
||||
@@ -764,7 +785,12 @@ export function evaluateExecAllowlist(params: {
|
||||
}
|
||||
const allowSkills = params.autoAllowSkills === true && (params.skillBins?.size ?? 0) > 0;
|
||||
const allowlistSatisfied = params.analysis.segments.every((segment) => {
|
||||
const match = matchAllowlist(params.allowlist, segment.resolution);
|
||||
const candidatePath = resolveAllowlistCandidatePath(segment.resolution, params.cwd);
|
||||
const candidateResolution =
|
||||
candidatePath && segment.resolution
|
||||
? { ...segment.resolution, resolvedPath: candidatePath }
|
||||
: segment.resolution;
|
||||
const match = matchAllowlist(params.allowlist, candidateResolution);
|
||||
if (match) allowlistMatches.push(match);
|
||||
const safe = isSafeBinUsage({
|
||||
argv: segment.argv,
|
||||
|
||||
@@ -184,9 +184,19 @@ export async function loadCostUsageSummary(params?: {
|
||||
|
||||
const sessionsDir = resolveSessionTranscriptsDirForAgent(params?.agentId);
|
||||
const entries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
|
||||
const files = entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
|
||||
.map((entry) => path.join(sessionsDir, entry.name));
|
||||
const files = (
|
||||
await Promise.all(
|
||||
entries
|
||||
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
|
||||
.map(async (entry) => {
|
||||
const filePath = path.join(sessionsDir, entry.name);
|
||||
const stats = await fs.promises.stat(filePath).catch(() => null);
|
||||
if (!stats) return null;
|
||||
if (stats.mtimeMs < sinceTime) return null;
|
||||
return filePath;
|
||||
}),
|
||||
)
|
||||
).filter((filePath): filePath is string => Boolean(filePath));
|
||||
|
||||
for (const filePath of files) {
|
||||
await scanUsageFile({
|
||||
|
||||
Reference in New Issue
Block a user