mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-07 17:04:42 +00:00
fix(hub): harden relationship lifecycle
Keep optional Hub authority from blocking daemon startup, revoke ambiguous enrollments durably, and close owned agents when their relationship no longer exists. Reject remote CLI connect targets before transmitting enrollment authority.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { assertLocalHubManagementTarget } from "./index.js";
|
||||
|
||||
describe("Hub CLI target authority", () => {
|
||||
test.each(["relay.paseo.test:443", "tcp://10.0.0.5:6767?ssl=true"])(
|
||||
"rejects non-local explicit target %s before connect",
|
||||
(host) => {
|
||||
expect(() => assertLocalHubManagementTarget(host, {})).toThrow(
|
||||
"Hub relationship management requires a local daemon target",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.each([
|
||||
undefined,
|
||||
"localhost:6767",
|
||||
"127.0.0.1:6767",
|
||||
"[::1]:6767",
|
||||
"[0:0:0:0:0:0:0:1]:6767",
|
||||
"unix:///tmp/paseo.sock",
|
||||
])("accepts local target %s", (host) => {
|
||||
expect(() => assertLocalHubManagementTarget(host, {})).not.toThrow();
|
||||
});
|
||||
|
||||
test("rejects a non-local PASEO_HOST target", () => {
|
||||
expect(() =>
|
||||
assertLocalHubManagementTarget(undefined, { PASEO_HOST: "hub.test:6767" }),
|
||||
).toThrow("Hub relationship management requires a local daemon target");
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Command } from "commander";
|
||||
import { withOutput, type ListResult, type OutputSchema } from "../../output/index.js";
|
||||
import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
|
||||
import { connectToDaemon } from "../../utils/client.js";
|
||||
import { connectToDaemon, resolveDaemonTarget } from "../../utils/client.js";
|
||||
|
||||
interface HubRow {
|
||||
state: string;
|
||||
@@ -66,6 +66,26 @@ async function withClient<T>(
|
||||
}
|
||||
}
|
||||
|
||||
export function assertLocalHubManagementTarget(
|
||||
host: string | undefined,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): void {
|
||||
const explicitHost = host ?? env.PASEO_HOST;
|
||||
if (!explicitHost) return;
|
||||
const target = resolveDaemonTarget(explicitHost);
|
||||
if (target.type === "ipc") return;
|
||||
const hostname = new URL(target.url).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
||||
if (
|
||||
hostname === "localhost" ||
|
||||
hostname === "::1" ||
|
||||
hostname === "0:0:0:0:0:0:0:1" ||
|
||||
/^127(?:\.\d{1,3}){3}$/.test(hostname)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
throw new Error("Hub relationship management requires a local daemon target");
|
||||
}
|
||||
|
||||
export function createHubCommand(): Command {
|
||||
const hub = new Command("hub").description("Manage this daemon's Paseo Hub relationship");
|
||||
addJsonAndDaemonHostOptions(
|
||||
@@ -74,6 +94,7 @@ export function createHubCommand(): Command {
|
||||
withOutput(async (...args) => {
|
||||
const url = args[0] as string;
|
||||
const options = args.at(-2) as { token: string; host?: string };
|
||||
assertLocalHubManagementTarget(options.host);
|
||||
return withClient(options.host, async (client) =>
|
||||
result((await client.connectHub(url, options.token)).status),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user