diff --git a/scripts/e2e/kitchen-sink-rpc-walk.mjs b/scripts/e2e/kitchen-sink-rpc-walk.mjs index a5d584a9e506..2baf8f83f620 100644 --- a/scripts/e2e/kitchen-sink-rpc-walk.mjs +++ b/scripts/e2e/kitchen-sink-rpc-walk.mjs @@ -1740,7 +1740,7 @@ function parsePosixProcessRows(stdout) { const processId = Number.parseInt(pidRaw, 10); const parentProcessId = Number.parseInt(ppidRaw, 10); const rssKb = Number.parseInt(rssKbRaw, 10); - const cpuPercent = parsePosixCpuPercent(cpuRaw); + const cpuPercent = parseStrictNonNegativeDecimal(cpuRaw); if ( !Number.isInteger(processId) || !Number.isInteger(parentProcessId) || @@ -1759,7 +1759,7 @@ function parsePosixProcessRows(stdout) { .filter(Boolean); } -function parsePosixCpuPercent(raw) { +function parseStrictNonNegativeDecimal(raw) { const text = String(raw ?? "").trim(); if (!/^(?:0|[1-9]\d*)(?:\.\d+)?$/u.test(text)) { return null; @@ -1768,6 +1768,15 @@ function parsePosixCpuPercent(raw) { return Number.isFinite(parsed) ? parsed : null; } +function parseStrictUnsignedInteger(raw) { + const text = String(raw ?? "").trim(); + if (!/^(?:0|[1-9]\d*)$/u.test(text)) { + return null; + } + const parsed = Number(text); + return Number.isSafeInteger(parsed) ? parsed : null; +} + function collectPosixProcessTree(rows, rootPid) { const byParent = new Map(); for (const row of rows) { @@ -1949,24 +1958,24 @@ async function sampleWindowsProcess(pid, run, commandLineNeedles = []) { const [workingSetBytesRaw, cpuSecondsRaw, processIdRaw, aggregateWorkingSetBytesRaw] = stdout .trim() .split(/\s+/u); - const workingSetBytes = Number.parseInt(workingSetBytesRaw ?? "", 10); - const aggregateWorkingSetBytes = Number.parseInt( + const workingSetBytes = parseStrictUnsignedInteger(workingSetBytesRaw); + const aggregateWorkingSetBytes = parseStrictUnsignedInteger( aggregateWorkingSetBytesRaw ?? workingSetBytesRaw ?? "", - 10, ); - const cpuSeconds = Number.parseFloat(cpuSecondsRaw ?? ""); - const processId = Number.parseInt(processIdRaw ?? "", 10); - if (!Number.isFinite(workingSetBytes)) { + const cpuSeconds = parseStrictNonNegativeDecimal(cpuSecondsRaw); + const processId = parseStrictUnsignedInteger(processIdRaw); + if (workingSetBytes === null) { return null; } return { rssMiB: Math.round((workingSetBytes / 1024 / 1024) * 10) / 10, - aggregateRssMiB: Number.isFinite(aggregateWorkingSetBytes) - ? Math.round((aggregateWorkingSetBytes / 1024 / 1024) * 10) / 10 - : Math.round((workingSetBytes / 1024 / 1024) * 10) / 10, + aggregateRssMiB: + aggregateWorkingSetBytes !== null + ? Math.round((aggregateWorkingSetBytes / 1024 / 1024) * 10) / 10 + : Math.round((workingSetBytes / 1024 / 1024) * 10) / 10, cpuPercent: null, - cpuSeconds: Number.isFinite(cpuSeconds) ? cpuSeconds : null, - processId: Number.isFinite(processId) ? processId : safePid, + cpuSeconds, + processId: processId ?? safePid, }; } catch { // Try the next Windows PowerShell command name. diff --git a/test/scripts/kitchen-sink-rpc-walk.test.ts b/test/scripts/kitchen-sink-rpc-walk.test.ts index 489577f0f84a..fd52f722ea75 100644 --- a/test/scripts/kitchen-sink-rpc-walk.test.ts +++ b/test/scripts/kitchen-sink-rpc-walk.test.ts @@ -1349,6 +1349,36 @@ describe("kitchen-sink RPC process sampling", () => { expect(sample?.aggregateRssMiB).toBe(96); }); + it("does not truncate malformed Windows PowerShell CPU or id samples", async () => { + const sample = await sampleProcess(1234, { + platform: "win32", + runCommand: async () => ({ + stdout: `${256 * 1024 * 1024} 2.25oops 6789x ${512 * 1024 * 1024}oops`, + stderr: "", + }), + }); + + expect(sample).toEqual({ + aggregateRssMiB: 256, + cpuPercent: null, + cpuSeconds: null, + processId: 1234, + rssMiB: 256, + }); + }); + + it("rejects malformed Windows PowerShell RSS samples", async () => { + const sample = await sampleProcess(1234, { + platform: "win32", + runCommand: async () => ({ + stdout: `${256 * 1024 * 1024}oops 2.25 6789 ${512 * 1024 * 1024}`, + stderr: "", + }), + }); + + expect(sample).toBeNull(); + }); + it("samples the Windows gateway process by listening port", async () => { const calls: Array<{ command: string; args: string[] }> = []; const sample = await sampleWindowsProcessByPort(19675, {