T05 · Unauthorized Access and Privilege Escalation
- Location
relayClient.ts:91- Finding
Privileged Relay Commands Are Accepted Before Authentication
- Content
View full analysis
Vulnerability Details
File Location:
relayClient.ts:58-62, 91-101
Vulnerability Type: Missing authentication-state authorization check
Risk Level: HighVulnerable Code
ts this.ws.onmessage = (event) => { try { const msg = JSON.parse(String(event.data)); this.handleMessage(msg); } catch { console.error("[privaclaw] Invalid message received"); } };ts private handleMessage(msg: { type: string; data?: unknown }): void { switch (msg.type) { case "hello_ok": console.log("[privaclaw] Authenticated"); this.backoff = INITIAL_BACKOFF; this.connectionState = "online"; this.startHeartbeat(); break; case "prompt": case "status": case "restart": case "workflow": this.dispatch(msg as { type: string; data: IncomingMessage }); break;Technical Analysis
The client marks the connection as authenticated only after receiving
hello_ok, but privileged messages are dispatched without checkingconnectionState === "online"or a separate authenticated-session flag.Consequently, the remote WebSocket peer can send a
prompt,status,restart, orworkflowmessage immediately after the socket opens and before authentication succeeds. The command is passed directly to the host runtime throughexecutePrompt,executeWorkflow, orrestart.This violates the documented guarantee that relay commands are accepted only while the node is authenticated and online. TypeScript type assertions do not provide runtime authorization or message validation.
Attack Path
- The node establishes a WebSocket connection to a relay peer.
- The client sends its authentication
hellomessage. - Before returning
hello_ok, the peer sends a validly formattedprompt,workflow, orrestartenvelope. onmessageparses the envelope and callshandleMessage.handleMessagedispatches the command without checking authenticated state.- The corresponding pri ...[truncated 703 chars]
- Remediation
View remediation
Remediation Suggestions
- Maintain an explicit authentication flag that starts as
false, becomestrueonly after a validhello_ok, and is reset on connection close, disconnect, and reconnection. - Before dispatching any privileged message, require both an authenticated flag and the expected online connection state.
- Reject or close the connection when privileged commands arrive before authentication.
- Treat repeated or out-of-order authentication messages as protocol violations.
- Validate incoming envelopes at runtime rather than relying on TypeScript casts.
- Add regression tests proving that
prompt,status,restart, andworkflowmessages received beforehello_okdo not invoke runtime methods.
Example hardening pattern:
ts private authenticated = false; private handleMessage(msg: { type: string; data?: unknown }): void { if (msg.type === "hello_ok") { this.authenticated = true; this.connectionState = "online"; this.startHeartbeat(); return; } if (!this.authenticated || this.connectionState !== "online") { console.warn("[privaclaw] Rejecting command before authentication"); return; } switch (msg.type) { case "prompt": case "status": case "restart": case "workflow": void this.dispatch(msg as { type: string; data: IncomingMessage }); break; } }- Maintain an explicit authentication flag that starts as
