mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 09:42:53 +00:00
Keep daemon credentials out of relay upgrades without E2EE
Restores the relay upgrade boundary removed in the boundary extraction: a credentialed DaemonClient on a relay URL with E2EE omitted sent authHeader to the relay. Regression test through the public DaemonClient API.
This commit is contained in:
@@ -37,6 +37,16 @@ export function createRelayE2eeTransportFactory(args: {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Relay upgrades carry routing only. Daemon credentials and subprotocols must
|
||||
* never reach the relay; they travel inside the encrypted hello.
|
||||
*/
|
||||
export function createRelayUpgradeTransportFactory(
|
||||
baseFactory: DaemonTransportFactory,
|
||||
): DaemonTransportFactory {
|
||||
return ({ url }) => baseFactory({ url });
|
||||
}
|
||||
|
||||
export function createEncryptedTransport(
|
||||
base: DaemonTransport,
|
||||
daemonPublicKeyB64: string,
|
||||
|
||||
@@ -17,6 +17,7 @@ export {
|
||||
export {
|
||||
createEncryptedTransport,
|
||||
createRelayE2eeTransportFactory,
|
||||
createRelayUpgradeTransportFactory,
|
||||
} from "./daemon-client-relay-e2ee-transport.js";
|
||||
export {
|
||||
bindWsHandler,
|
||||
|
||||
@@ -919,6 +919,29 @@ test("keeps relay upgrade credentials out of the socket request", async () => {
|
||||
await expect(connecting).rejects.toThrow("Daemon client closed");
|
||||
});
|
||||
|
||||
test("keeps relay upgrade credentials out of the socket request without E2EE", async () => {
|
||||
const mock = createMockTransport();
|
||||
const requests: Array<{ url: string; headers?: Record<string, string>; protocols?: string[] }> =
|
||||
[];
|
||||
const client = new DaemonClient({
|
||||
url: "ws://relay.test/ws?role=client&serverId=srv_test&v=2",
|
||||
clientId: "clsk_relay_header_test",
|
||||
authHeader: "Bearer synthetic-review-token",
|
||||
reconnect: { enabled: false },
|
||||
transportFactory: (request) => {
|
||||
requests.push(request);
|
||||
return mock.transport;
|
||||
},
|
||||
});
|
||||
clients.push(client);
|
||||
const connecting = client.connect();
|
||||
void connecting.catch(() => {});
|
||||
await vi.waitFor(() => expect(requests).toHaveLength(1));
|
||||
expect(requests[0]).toEqual({ url: "ws://relay.test/ws?role=client&serverId=srv_test&v=2" });
|
||||
await client.close();
|
||||
await expect(connecting).rejects.toThrow("Daemon client closed");
|
||||
});
|
||||
|
||||
test("refuses relay password auth without an encrypted hello", async () => {
|
||||
const requests: unknown[] = [];
|
||||
const client = new DaemonClient({
|
||||
|
||||
@@ -162,6 +162,7 @@ import {
|
||||
} from "@getpaseo/protocol/binary-frames/index";
|
||||
import {
|
||||
createRelayE2eeTransportFactory,
|
||||
createRelayUpgradeTransportFactory,
|
||||
createWebSocketTransportFactory,
|
||||
decodeMessageData,
|
||||
defaultWebSocketFactory,
|
||||
@@ -1449,8 +1450,11 @@ export class DaemonClient {
|
||||
const isRelayTransport = isRelayClientWebSocketUrl(this.config.url);
|
||||
this.assertEncryptedRelayAuth(selected.helloAuth, isRelayTransport);
|
||||
|
||||
// Relay upgrades are routing only: credentials never reach the relay socket.
|
||||
let transportFactory = isRelayTransport
|
||||
? createRelayUpgradeTransportFactory(baseTransportFactory)
|
||||
: baseTransportFactory;
|
||||
// Endpoint security is selected by configuration, never inferred from the URL.
|
||||
let transportFactory = baseTransportFactory;
|
||||
if (this.config.e2ee?.enabled === true) {
|
||||
const daemonPublicKeyB64 = this.config.e2ee.daemonPublicKeyB64;
|
||||
if (!daemonPublicKeyB64) {
|
||||
|
||||
Reference in New Issue
Block a user