fix(hub): validate persisted origins

This commit is contained in:
Mohamed Boudra
2026-07-13 18:48:13 +02:00
parent f561f628b7
commit 6472d9dcbd
2 changed files with 49 additions and 3 deletions
@@ -227,6 +227,38 @@ describe("Hub relationship", () => {
},
);
test.each([
["a non-HTTP scheme", "ftp://hub.test"],
["embedded credentials", "https://user:password@hub.test"],
["a query", "https://hub.test?token=secret"],
["a fragment", "https://hub.test#secret"],
])("a persisted Hub origin with %s is quarantined before startup", async (_, hubOrigin) => {
relationship = await HubRelationshipHarness.start();
await relationship.corruptRelationshipFile(
JSON.stringify({
version: 1,
state: "pending",
relationship: {
daemonId: "daemon-1",
idempotencyKey: "ceremony-1",
hubOrigin,
createdAt: "2026-07-13T00:00:00.000Z",
scopes: ["hub.execution.*"],
},
credential: { secret: "credential" },
enrollment: { token: "enrollment-token" },
identity: { serverId: "server-1", daemonPublicKey: "public-key" },
}),
);
await relationship.startStoppedDaemon();
expect(await relationship.status()).toMatchObject({ state: "not_connected" });
expect(relationship.relationshipFile()).toBeNull();
expect(await relationship.quarantinedRelationshipFiles()).toHaveLength(1);
expect(relationship.enrollmentAttempts()).toEqual([]);
});
test("a persisted non-WebSocket transport is quarantined before daemon startup", async () => {
relationship = await HubRelationshipHarness.start();
await relationship.corruptRelationshipFile(
@@ -16,11 +16,24 @@ import { BoundedExponentialHubRetryPolicy } from "./relationship-retry.js";
const FILE_NAME = "hub-relationship.json";
const SCOPES = ["hub.execution.*"] as const;
const HubOriginSchema = z
.string()
.url()
.superRefine((value, context) => {
try {
normalizeHubUrl(value);
} catch (error) {
context.addIssue({
code: "custom",
message: error instanceof Error ? error.message : "Invalid Hub URL",
});
}
});
const RelationshipSchema = z.object({
daemonId: z.string().min(1),
idempotencyKey: z.string().min(1),
hubOrigin: z.string().url(),
hubOrigin: HubOriginSchema,
createdAt: z.string(),
scopes: z.array(z.string()),
});
@@ -153,9 +166,10 @@ function normalizeHubUrl(value: string): string {
if (url.username || url.password) {
throw new Error("Hub URL cannot include credentials");
}
if (url.search || url.hash) {
throw new Error("Hub URL cannot include a query or fragment");
}
url.pathname = url.pathname.replace(/\/$/, "");
url.search = "";
url.hash = "";
return url.toString().replace(/\/$/, "");
}