mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-22 17:22:47 +00:00
fix(cli): preserve validated addresses in daemon command hints
This commit is contained in:
@@ -15,7 +15,7 @@ describe("CLI SSH transport", () => {
|
||||
expect(parseHostTarget("ssh://deploy@build-box:2222?daemonPort=7777")).toEqual({
|
||||
kind: "ssh",
|
||||
ssh: { host: "deploy@build-box", sshPort: 2222, daemonPort: 7777 },
|
||||
description: "ssh://deploy@build-box:2222 (daemon port 7777)",
|
||||
description: "ssh://build-box:2222 (daemon port 7777)",
|
||||
});
|
||||
expect(() => parseHostTarget("ssh://deploy:secret@build-box")).toThrow(
|
||||
"Invalid host connection",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { parseHostTarget } from "@getpaseo/protocol/host-target";
|
||||
import { test, expect } from "vitest";
|
||||
import { selectDaemonTarget, describeDaemonTarget } from "./daemon-target.js";
|
||||
import {
|
||||
@@ -48,6 +49,19 @@ test("endpoint descriptions redact pairing material and credentials", () => {
|
||||
).not.toContain("private");
|
||||
});
|
||||
|
||||
test("a pairing offer without a URL prefix still redacts its key", () => {
|
||||
const offer = {
|
||||
v: 2,
|
||||
serverId: "srv_example",
|
||||
daemonPublicKeyB64: "example-secret",
|
||||
relay: { endpoint: "localhost:6767", useTls: false },
|
||||
};
|
||||
const host = `#offer=${Buffer.from(JSON.stringify(offer)).toString("base64url")}`;
|
||||
expect(describeDaemonTarget({ kind: "endpoint", host })).toBe(
|
||||
"relay://localhost:6767/srv_example",
|
||||
);
|
||||
});
|
||||
|
||||
test("malformed endpoint diagnostics never expose the supplied credential", () => {
|
||||
const target = { kind: "endpoint" as const, host: "alice:example-secret@localhost:6767" };
|
||||
expect(describeDaemonTarget(target)).not.toContain("example-secret");
|
||||
@@ -83,3 +97,34 @@ test("CLI selects an explicit password before a matching local credential and ne
|
||||
await rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test.each(["/tmp/paseo.sock", "\\\\.\\pipe\\paseo", "localhost:6767", "[::1]:6767", "6767"])(
|
||||
"validated bare target %s keeps its spelling in command hints",
|
||||
(host) => {
|
||||
expect(describeDaemonTarget({ kind: "endpoint", host })).toBe(host);
|
||||
},
|
||||
);
|
||||
|
||||
test("validated URI command hints preserve their transport address without credentials", () => {
|
||||
const ssh = describeDaemonTarget({
|
||||
kind: "endpoint",
|
||||
host: "ssh://deploy@build-box:2222?daemonPort=7777&password=example-secret",
|
||||
});
|
||||
expect(ssh).not.toContain("example-secret");
|
||||
expect(parseHostTarget(ssh)).toMatchObject({
|
||||
kind: "ssh",
|
||||
ssh: { host: "deploy@build-box", sshPort: 2222, daemonPort: 7777 },
|
||||
});
|
||||
const ws = describeDaemonTarget({
|
||||
kind: "endpoint",
|
||||
host: "wss://example.test/custom-ws?authorization=example-secret",
|
||||
});
|
||||
expect(ws).not.toContain("example-secret");
|
||||
expect(new URL(ws).pathname).toBe("/custom-ws");
|
||||
expect(
|
||||
describeDaemonTarget({
|
||||
kind: "endpoint",
|
||||
host: "tcp://localhost:6767?daemonPort=example-secret&ssl=example-secret",
|
||||
}),
|
||||
).not.toContain("example-secret");
|
||||
});
|
||||
|
||||
@@ -40,7 +40,25 @@ export function selectDaemonTarget(
|
||||
export function describeDaemonTarget(target: DaemonTarget): string {
|
||||
if (target.kind === "instance") return `home ${target.home}`;
|
||||
try {
|
||||
return parseHostTarget(target.host).description;
|
||||
const input = target.host.trim();
|
||||
const parsed = parseHostTarget(input);
|
||||
// Discovery descriptions are display text; command hints must preserve the address syntax.
|
||||
if (parsed.kind === "ssh" || (!parsed.offer && !parsed.socketPath && input.includes("://"))) {
|
||||
const url = new URL(input);
|
||||
if (decodeURIComponent(url.username).includes(":")) url.username = "REDACTED";
|
||||
for (const key of new Set(url.searchParams.keys())) {
|
||||
const routingOption =
|
||||
(parsed.kind === "ssh" && key === "daemonPort") ||
|
||||
(key === "ssl" &&
|
||||
url.searchParams.getAll(key).every((value) => value === "true" || value === "false"));
|
||||
if (!routingOption) url.searchParams.set(key, "REDACTED");
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
// Pairing offers must always use safe display text, including prefix-free fragments.
|
||||
return parsed.kind === "socket" && !parsed.offer && !input.includes("://")
|
||||
? input
|
||||
: parsed.description;
|
||||
} catch {
|
||||
return "<invalid endpoint>";
|
||||
}
|
||||
|
||||
@@ -445,6 +445,7 @@ test("CLI host commands manage the selected daemon and emit secret-free structur
|
||||
await promisify(execFile)(
|
||||
process.execPath,
|
||||
[
|
||||
"--conditions=source",
|
||||
"--import",
|
||||
"tsx",
|
||||
fileURLToPath(new URL("../../../../cli/src/index.ts", import.meta.url)),
|
||||
|
||||
Reference in New Issue
Block a user