fix(migrate): validate workspace connection targets

This commit is contained in:
Mohamed Boudra
2026-07-19 01:10:18 +02:00
parent e61c06706c
commit faccbaae59
4 changed files with 78 additions and 5 deletions
+15
View File
@@ -43,6 +43,21 @@ test("discovers the running daemon TCP address from its PID record", () => {
]);
});
test("normalizes the daemon's bare IPv6 loopback PID address", () => {
const paseoHome = mkdtempSync(path.join(os.tmpdir(), "paseo-node-client-pid-ipv6-"));
cleanup.push(paseoHome);
writeFileSync(path.join(paseoHome, "paseo.pid"), JSON.stringify({ listen: "::1:7789" }));
expect(resolveDefaultDaemonHosts({ PASEO_HOME: paseoHome })).toEqual([
"[::1]:7789",
"localhost:6767",
]);
expect(resolveDaemonTarget("::1:7789")).toEqual({
type: "tcp",
url: "ws://[::1]:7789/ws",
});
});
test("normalizes TCP, Unix, pipe, and Windows path-shaped targets", () => {
expect(normalizeDaemonHost("tcp://Example.com:6767?ssl=true&password=secret")).toBe(
"tcp://Example.com:6767?ssl=true&password=secret",
+15 -1
View File
@@ -89,6 +89,9 @@ export function normalizeDaemonHost(raw: string): string | null {
if (value.startsWith("/") || value.startsWith("~")) return `unix://${value}`;
if (/^[A-Za-z]:[/\\]/.test(value)) return null;
if (/^\d+$/.test(value)) return `127.0.0.1:${value}`;
const ipv6Loopback = normalizeBareIpv6Loopback(value);
if (ipv6Loopback) return ipv6Loopback;
if (value.startsWith("::1:")) return null;
return value.includes(":") ? value : null;
}
@@ -166,7 +169,18 @@ export function resolveDaemonTarget(host: string): DaemonTarget {
);
return { type: "tcp", url: buildDaemonWebSocketUrl(endpoint, { useTls: parsed.useTls }) };
}
return { type: "tcp", url: `ws://${value}/ws` };
const endpoint = normalizeBareIpv6Loopback(value) ?? normalizeHostPort(value);
return { type: "tcp", url: buildDaemonWebSocketUrl(endpoint, { useTls: false }) };
}
function normalizeBareIpv6Loopback(value: string): string | null {
const match = value.match(/^::1:(\d{1,5})$/);
if (!match) return null;
try {
return normalizeHostPort(`[::1]:${match[1]}`);
} catch {
return null;
}
}
export function resolveDaemonPassword(