From 3ca11355153a123e0711d0aa13d240cc5ce8f09b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 28 May 2026 23:56:50 +0200 Subject: [PATCH] fix(e2e): reject loose runtime smoke limits --- .../runtime-smoke.mjs | 6 ++- ...led-plugin-install-uninstall-probe.test.ts | 51 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs index 31e444b95bda..29649f9f020a 100644 --- a/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs +++ b/scripts/e2e/lib/bundled-plugin-install-uninstall/runtime-smoke.mjs @@ -42,7 +42,11 @@ const READY_OFFSET_LOG_NEEDLES = [ const FORBIDDEN_POST_READY_DEPS_WORK = [/\b(?:npm|pnpm|yarn|corepack) install\b/iu]; function readPositiveInt(raw, fallback) { - const parsed = Number.parseInt(String(raw || ""), 10); + const text = String(raw ?? "").trim(); + if (!/^\d+$/u.test(text)) { + return fallback; + } + const parsed = Number(text); return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; } diff --git a/test/scripts/bundled-plugin-install-uninstall-probe.test.ts b/test/scripts/bundled-plugin-install-uninstall-probe.test.ts index b9800b638351..6d477aa0e218 100644 --- a/test/scripts/bundled-plugin-install-uninstall-probe.test.ts +++ b/test/scripts/bundled-plugin-install-uninstall-probe.test.ts @@ -95,6 +95,31 @@ function runRuntimeSmoke(root: string, args: string[]) { }); } +async function importRuntimeSmokeWithEnv(env: Record) { + const previous = new Map(); + for (const [key, value] of Object.entries(env)) { + previous.set(key, process.env[key]); + if (value === undefined) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } + try { + return await import( + `${pathToFileURL(runtimeSmokePath).href}?case=${Date.now()}-${Math.random()}` + ); + } finally { + for (const [key, value] of previous.entries()) { + if (value === undefined) { + delete process.env[key]; + } else { + process.env[key] = value; + } + } + } +} + async function listenOnLoopback(server: HttpServer | NetServer): Promise { return new Promise((resolve, reject) => { const onError = (error: Error) => { @@ -151,6 +176,17 @@ describe("bundled plugin install/uninstall probe", () => { expect(second).toEqual({ text: "fghij", truncatedChars: 5 }); }); + it("rejects loose runtime output limit env values instead of parsing prefixes", async () => { + const runtimeSmoke = await importRuntimeSmokeWithEnv({ + OPENCLAW_BUNDLED_PLUGIN_RUNTIME_OUTPUT_CHARS: "5chars", + }); + + expect(runtimeSmoke.appendBoundedOutput({ text: "", truncatedChars: 0 }, "abcdef")).toEqual({ + text: "abcdef", + truncatedChars: 0, + }); + }); + it("keeps runtime log tail reads bounded", async () => { const runtimeSmoke = await import(pathToFileURL(runtimeSmokePath).href); const root = makePackageRoot(); @@ -165,6 +201,21 @@ describe("bundled plugin install/uninstall probe", () => { expect(fullRead).not.toHaveBeenCalled(); }); + it("rejects loose runtime log scan byte env values instead of parsing prefixes", async () => { + const runtimeSmoke = await importRuntimeSmokeWithEnv({ + OPENCLAW_BUNDLED_PLUGIN_RUNTIME_LOG_SCAN_BYTES: "64bytes", + }); + const root = makePackageRoot(); + const logPath = path.join(root, "gateway.log"); + fs.writeFileSync(logPath, `${"old log line\n".repeat(20)}[gateway] ready\n`, "utf8"); + + const tail = runtimeSmoke.readFileTail(logPath); + + expect(Buffer.byteLength(tail)).toBeGreaterThan(64); + expect(tail).toContain("old log line"); + expect(tail).toContain("[gateway] ready"); + }); + it("remembers runtime ready logs after they fall outside the tail", async () => { const runtimeSmoke = await import(pathToFileURL(runtimeSmokePath).href); const root = makePackageRoot();