fix(agent-core): cap shell exec timeouts

This commit is contained in:
Peter Steinberger
2026-05-30 02:40:32 -04:00
parent 4abde61366
commit 7840fdbada
2 changed files with 43 additions and 4 deletions

View File

@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
import { resolveExecTimeoutMs } from "./nodejs.js";
describe("NodeExecutionEnv timeout helpers", () => {
it("converts positive timeout seconds to milliseconds", () => {
expect(resolveExecTimeoutMs(1)).toBe(1_000);
expect(resolveExecTimeoutMs(1.5)).toBe(1_500);
expect(resolveExecTimeoutMs(0.0005)).toBe(1);
});
it("caps oversized timeout seconds to a timer-safe delay", () => {
expect(resolveExecTimeoutMs(Number.MAX_SAFE_INTEGER)).toBe(2_147_000_000);
});
it("ignores absent, invalid, or non-positive timeout seconds", () => {
expect(resolveExecTimeoutMs(undefined)).toBeUndefined();
expect(resolveExecTimeoutMs(Number.NaN)).toBeUndefined();
expect(resolveExecTimeoutMs(0)).toBeUndefined();
expect(resolveExecTimeoutMs(-1)).toBeUndefined();
});
});

View File

@@ -29,10 +29,27 @@ import {
} from "../types.js";
import { killProcessTree } from "./kill-tree.js";
const MAX_TIMER_TIMEOUT_MS = 2_147_000_000;
function resolvePath(cwd: string, path: string): string {
return isAbsolute(path) ? path : resolve(cwd, path);
}
export function resolveExecTimeoutMs(timeoutSeconds: unknown): number | undefined {
if (
typeof timeoutSeconds !== "number" ||
!Number.isFinite(timeoutSeconds) ||
timeoutSeconds <= 0
) {
return undefined;
}
const milliseconds = Math.floor(timeoutSeconds * 1000);
if (!Number.isFinite(milliseconds) || milliseconds <= 0) {
return 1;
}
return Math.min(milliseconds, MAX_TIMER_TIMEOUT_MS);
}
function fileKindFromStats(stats: {
isFile(): boolean;
isDirectory(): boolean;
@@ -309,15 +326,16 @@ export class NodeExecutionEnv implements ExecutionEnv {
return;
}
const timeoutMs = resolveExecTimeoutMs(options?.timeout);
timeoutId =
typeof options?.timeout === "number"
? setTimeout(() => {
timeoutMs === undefined
? undefined
: setTimeout(() => {
timedOut = true;
if (child?.pid) {
killProcessTree(child.pid, { force: true });
}
}, options.timeout * 1000)
: undefined;
}, timeoutMs);
if (options?.abortSignal) {
if (options.abortSignal.aborted) {