mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-07 17:04:42 +00:00
fix(hosts): validate safe targets and verify encrypted relay routes
This commit is contained in:
@@ -42,15 +42,15 @@ export function buildDaemonConnectionCommandError(options: ConnectOptions & { er
|
||||
if (error && typeof error === "object" && "message" in error) message = String(error.message);
|
||||
if (options.target.kind === "endpoint")
|
||||
message = message.replaceAll(options.target.host, describeDaemonTarget(options.target));
|
||||
if (message.startsWith("Cannot connect to daemon at "))
|
||||
return error as { code: string; message: string; details: string };
|
||||
let code = "DAEMON_UNREACHABLE";
|
||||
if (typeof error === "object" && error !== null && "code" in error) code = String(error.code);
|
||||
else if (message === "Password required") code = "AUTH_REQUIRED";
|
||||
else if (message === "Incorrect password") code = "AUTH_FAILED";
|
||||
return {
|
||||
code,
|
||||
message: `Cannot connect to daemon at ${describeDaemonTarget(options.target)}: ${message}`,
|
||||
message: message.startsWith("Cannot connect to daemon at ")
|
||||
? message
|
||||
: `Cannot connect to daemon at ${describeDaemonTarget(options.target)}: ${message}`,
|
||||
details: describeConnectionRemedy(code, options.target),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { test, expect } from "vitest";
|
||||
import { selectDaemonTarget, describeDaemonTarget } from "./daemon-target.js";
|
||||
import { resolveDaemonCredential, resolveClientPaseoHome } from "./client.js";
|
||||
import {
|
||||
resolveDaemonCredential,
|
||||
resolveClientPaseoHome,
|
||||
buildDaemonConnectionCommandError,
|
||||
} from "./client.js";
|
||||
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -44,6 +48,18 @@ test("endpoint descriptions redact pairing material and credentials", () => {
|
||||
).not.toContain("private");
|
||||
});
|
||||
|
||||
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");
|
||||
for (const message of [
|
||||
`Invalid connection ${target.host}`,
|
||||
`Cannot connect to daemon at ${target.host}`,
|
||||
]) {
|
||||
const diagnostic = buildDaemonConnectionCommandError({ target, error: new Error(message) });
|
||||
expect(JSON.stringify(diagnostic)).not.toContain("example-secret");
|
||||
}
|
||||
});
|
||||
|
||||
test("CLI selects an explicit password before a matching local credential and never sends the latter remotely", async () => {
|
||||
const home = await mkdtemp(join(tmpdir(), "paseo-cli-credential-"));
|
||||
const previousPassword = process.env.PASEO_PASSWORD;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { parseHostTarget } from "@getpaseo/protocol/host-target";
|
||||
import { resolvePaseoHome } from "@getpaseo/server/daemon-control";
|
||||
|
||||
export type DaemonTarget = { kind: "instance"; home: string } | { kind: "endpoint"; host: string };
|
||||
@@ -39,13 +40,8 @@ export function selectDaemonTarget(
|
||||
export function describeDaemonTarget(target: DaemonTarget): string {
|
||||
if (target.kind === "instance") return `home ${target.home}`;
|
||||
try {
|
||||
const url = new URL(target.host);
|
||||
if (url.password) url.password = "REDACTED";
|
||||
for (const key of url.searchParams.keys())
|
||||
if (/password|token|secret/i.test(key)) url.searchParams.set(key, "REDACTED");
|
||||
if (url.hash) url.hash = "REDACTED";
|
||||
return url.toString();
|
||||
return parseHostTarget(target.host).description;
|
||||
} catch {
|
||||
return target.host.replace(/([?&](?:password|token|secret)=)[^&]*/gi, "$1REDACTED");
|
||||
return "<invalid endpoint>";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user