From 2afad06fde02e4e06d380e8a7715c3eacaed9ac7 Mon Sep 17 00:00:00 2001 From: Ayush7614 Date: Sun, 6 Sep 2026 01:16:57 +0530 Subject: [PATCH] fix(supervisor): validate HostPort digits and range instead of bare parseInt --- supervisor/src/docker.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/supervisor/src/docker.ts b/supervisor/src/docker.ts index ce334cac..19bafbec 100644 --- a/supervisor/src/docker.ts +++ b/supervisor/src/docker.ts @@ -150,6 +150,20 @@ function portOf(ports?: Docker.Port[] | undefined): number | undefined { return published ?? undefined; } +/** + * A published port from `inspect`, which Docker reports as a string. + * + * The daemon hands back `""` before a port is assigned and anything at all when it + * misbehaves, so a bare `parseInt` turns `"abc"` into a `NaN` port and `"0"` into a + * port nothing can dial. Only digits in range are a port; anything else is no port. + */ +export function parseHostPort(value: unknown): number | undefined { + if (typeof value !== "string" || !/^\d+$/.test(value)) return undefined; + const port = Number.parseInt(value, 10); + if (!Number.isSafeInteger(port) || port < 1 || port > 65535) return undefined; + return port; +} + /** * Whether a labelled thing belongs to this deployment. * @@ -209,9 +223,10 @@ async function inspectOwned(names: ComputerNames): Promise<{ if (!ours(info.Config?.Labels)) return null; const published = info.NetworkSettings?.Ports?.[COMPUTER_PORT]?.[0]?.HostPort; + const port = parseHostPort(published); return { status: info.State?.Status ?? "unknown", - ...(published ? { port: Number.parseInt(published, 10) } : {}), + ...(port !== undefined ? { port } : {}), // The resolved image, not the tag it was started from. A tag moves when the image is // rebuilt; this is what the container is actually running. ...(info.Image ? { image: info.Image } : {}),