Back to skill

Security audit

PrivaClaw

Security checks for vulnerabilities and agentic risk

Overview

This remote-control skill is mostly coherent, but its code contradicts its security claims in ways that could expose node control and tokens.

Review this carefully before installing. Use only a relay operator you trust, require wss://, keep the auth token secret and rotated, and avoid production use until the maintainer adds an authenticated-state gate before command dispatch and clearer command-level authorization controls.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
relayClient.ts:91
Finding
Privileged Relay Commands Are Accepted Before Authentication## Vulnerability Details **File Location**: `relayClient.ts:58-62, 91-101` **Vulnerability Type**: Missing authentication-state authorization check **Risk Level**: High ### Vulnerable 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 checking `connectionState === "online"` or a separate authenticated-session flag. Consequently, the remote WebSocket peer can send a `prompt`, `status`, `restart`, or `workflow` message immediately after the socket opens and before authentication succeeds. The command is passed directly to the host runtime through `executePrompt`, `executeWorkflow`, or `restart`. 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 1. The node establishes a WebSocket connection to a relay peer. 2. The client sends its authentication `hello` message. 3. Before returning `hello_ok`, the peer sends a validly formatted `prompt`, `workflow`, or `restart` envelope. 4. `onmessage` parses the envelope and calls `handleMessage`. 5. `handleMessage` dispatches the command without checking authenticated state. 6. The corresponding pri ...[truncated 703 chars]
Remediation
## Remediation Suggestions - Maintain an explicit authentication flag that starts as `false`, becomes `true` only after a valid `hello_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`, and `workflow` messages received before `hello_ok` do 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; } } ```

T09 · Insecure Skill Coding Practices

Error
Location
config.ts:7
Finding
Plaintext WebSocket Configuration Exposes Authentication and Control Traffic## Vulnerability Details **File Location**: `config.ts:7-21` **Vulnerability Type**: Plaintext transmission of sensitive authentication and remote-control data **Risk Level**: High ### Vulnerable Code ```ts export function validateConfig(config: Partial<RemoteRelayConfig>): RemoteRelayConfig { if (!config.relay_url) throw new Error("privaclaw: relay_url is required"); if (!config.node_id) throw new Error("privaclaw: node_id is required"); if (!config.auth_token) throw new Error("privaclaw: auth_token is required"); // Normalize URL: ensure wss:// for secure connections let url = config.relay_url.replace(/\/+$/, ""); url = url.replace(/^https:\/\//, "wss://"); url = url.replace(/^http:\/\//, "ws://"); return { relay_url: url, node_id: config.node_id, auth_token: config.auth_token, }; } ``` The plaintext behavior is explicitly confirmed by `config.test.ts:17-20`: ```ts it("converts http to ws", () => { const result = validateConfig({ ...valid, relay_url: "http://localhost:8080" }); expect(result.relay_url).toBe("ws://localhost:8080"); }); ``` The credential is subsequently sent over the selected connection in `relayClient.ts:40-51`: ```ts this.ws.onopen = () => { console.log("[privaclaw] Connected, sending auth"); this.send({ type: "hello", data: { node_id: this.config.node_id, token: this.config.auth_token, meta: { kind: "openclaw" }, }, }); }; ``` ### Technical Analysis Configuration validation accepts `http://` and deliberately converts it to `ws://`. WebSocket connections using `ws://` do not provide TLS confidentiality, server authentication, or transport integrity. The client then sends the authentication token and node ID in the initial application message. The same connection carries remote commands, heartbeat information, prompt responses, workflow status, and error data. A network-positioned attacker can therefore observe or modify traffic, steal the reusable authenticatio ...[truncated 1392 chars]
Remediation
## Remediation Suggestions - Parse the relay address with the standard `URL` API. - Permit only the `wss:` scheme in production. - Reject `http:`, `ws:`, malformed URLs, URL-embedded credentials, fragments, and all unsupported schemes. - Do not silently downgrade `http://` to `ws://`. - If plaintext transport is required for development, place it behind an explicit development-only option and restrict it to verified loopback hosts such as `127.0.0.1`, `[::1]`, or `localhost`. - Clearly warn when any development exception is enabled. - Consider certificate pinning or mutually authenticated TLS for deployments with stronger relay-authentication requirements. - Replace the existing plaintext-acceptance test with tests asserting that insecure schemes are rejected. Example validation pattern: ```ts const parsed = new URL(config.relay_url); if (parsed.protocol !== "wss:") { throw new Error("privaclaw: relay_url must use wss://"); } if (parsed.username || parsed.password) { throw new Error("privaclaw: relay_url must not contain credentials"); } parsed.pathname = parsed.pathname.replace(/\/+$/, ""); return { relay_url: parsed.toString().replace(/\/$/, ""), node_id: config.node_id, auth_token: config.auth_token, }; ```
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code chunk does support a relay/control scenario, but the declared description is materially incomplete about what the relay actually enables. The interfaces explicitly provide remote command-and-control capabilities: executing prompts, executing workflows/tasks, streaming prompt tokens back, reporting status/errors, and restarting the OpenClaw process. The description frames this mainly as a 'secure outbound-only relay' and mentions networking constraints ('no exposed ports, no SSH, no Telegram'), but it does not clearly disclose these powerful remote execution and restart capabilities. There is no evidence here about exposed ports, SSH, or Telegram one way or the other, since this chunk only defines types/interfaces. The main mismatch is omission of the concrete remote execution/control behaviors present in the code.

Ae1

High
Category
analysis-evasion
Content
files: ["relayClient.ts", "config.ts", "capabilities.ts", "index.ts"]
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Vague Triggers

Medium
Confidence
90% confidence
Finding
The manifest advertises broad remote-control capabilities such as sending prompts, triggering workflows, checking health, and restarting the node, but it does not describe any authorization boundaries, allowed callers, or operational constraints. For a remote administration skill, this omission is security-relevant because it normalizes powerful control actions without clarifying safeguards, increasing the risk of unsafe deployment or accidental overexposure of node control.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The client accepts privileged remote actions from any party that can send valid WebSocket messages after connection, including prompt execution, workflow execution, and restart, with no local authorization gate, operator confirmation, or command-level access control. In this skill’s context, the feature is intentionally designed for remote OpenClaw control, which makes the behavior expected, but it also makes compromise of the relay, token theft, or misconfiguration immediately translate into remote code/task execution capability on the node.

Static analysis

No suspicious patterns detected.