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 };