From 15bb4874bf388e2e6071e7991eb0a66ef115b066 Mon Sep 17 00:00:00 2001 From: lizeyu Date: Wed, 1 Jul 2026 16:08:20 +0800 Subject: [PATCH] fix(ports): validate lsof PID parsing before assignment (#98371) * fix(ports): validate lsof PID parsing before assignment Add Number.isFinite guard on lsof 'p' line PID parsing in parseLsofOutput, consistent with the netstat branch in the same function which already validates with Number.isNaN. While the downstream if (current.pid) truthiness check catches NaN (falsy), adding validation at the parse site is defense-in- depth and eliminates an inconsistency within the same function. * test(ports): add edge case tests for parseLsofOutput Add test coverage for malformed lsof 'p' lines (empty PID, non-numeric suffix), zero PID, and empty input to verify the Number.isFinite guard correctly filters invalid entries. --- src/cli/ports.ts | 3 ++- src/cli/program.force.test.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/cli/ports.ts b/src/cli/ports.ts index 47563665ab4b..73cc6b76b5b9 100644 --- a/src/cli/ports.ts +++ b/src/cli/ports.ts @@ -148,7 +148,8 @@ export function parseLsofOutput(output: string): PortProcess[] { if (current.pid) { results.push(current as PortProcess); } - current = { pid: Number.parseInt(line.slice(1), 10) }; + const rawPid = Number.parseInt(line.slice(1), 10); + current = Number.isFinite(rawPid) && rawPid > 0 ? { pid: rawPid } : {}; } else if (line.startsWith("c")) { current.command = line.slice(1); } diff --git a/src/cli/program.force.test.ts b/src/cli/program.force.test.ts index 74ee37c2fd72..ed3414772c55 100644 --- a/src/cli/program.force.test.ts +++ b/src/cli/program.force.test.ts @@ -53,6 +53,35 @@ describe("gateway --force helpers", () => { ]); }); + it("skips malformed lsof 'p' lines (no digits after p)", () => { + const sample = ["p", "cnode", "p456", "cpython", ""].join("\n"); + const parsed = parseLsofOutput(sample); + expect(parsed).toEqual([{ pid: 456, command: "python" }]); + }); + + it("skips malformed lsof 'p' lines (non-numeric suffix)", () => { + const sample = ["pabc", "cnode", "p456", "cpython", ""].join("\n"); + const parsed = parseLsofOutput(sample); + expect(parsed).toEqual([{ pid: 456, command: "python" }]); + }); + + it("returns empty array when all lsof 'p' lines are malformed", () => { + const sample = ["p", "cnode", "pabc", "", ""].join("\n"); + const parsed = parseLsofOutput(sample); + expect(parsed).toEqual([]); + }); + + it("handles empty lsof output", () => { + expect(parseLsofOutput("")).toEqual([]); + }); + + it("handles 'p' lines with negative-like tokens (zero)", () => { + const sample = ["p0", "cnode", "p456", "cpython", ""].join("\n"); + const parsed = parseLsofOutput(sample); + // PID 0 is filtered out (> 0 check), only valid PIDs remain + expect(parsed).toEqual([{ pid: 456, command: "python" }]); + }); + it("returns empty list when lsof finds nothing", () => { (execFileSync as unknown as Mock).mockImplementation(() => { const err = new Error("no matches") as NodeJS.ErrnoException & { status?: number };