Back to skill

Security audit

deprecated ignore

Security checks for vulnerabilities and agentic risk

Overview

This hosted voice bridge is mostly transparent about its purpose, but it gives the relay broad ability to submit prompts to your local OpenClaw agent and uses install/dependency steps that are not tightly verifiable.

Review this carefully before installing. Use it only if you are comfortable with a hosted relay receiving voice transcripts and agent responses, and with that relay being able to submit prompts to your local OpenClaw agent while the bridge is running. Prefer a self-hosted relay or local alternative for sensitive work, avoid agents with broad file, shell, credential, or account access, and install only from a versioned artifact with an integrity check or lockfile-backed dependencies.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
lib/relay-client.js:31
Finding
Relay Messages Can Trigger Unauthenticated Local Agent Invocations## Vulnerability Details **File Location**: `lib/relay-client.js:31-56`, `bin/voice-bridge.js:111-123`, `lib/agent-bridge.js:18-35` **Vulnerability Type**: Missing message authentication and session validation **Risk Level**: High ### Vulnerable Code `lib/relay-client.js:31-56`: ```js this.ws.on('message', (raw) => { let msg; try { msg = JSON.parse(raw); } catch { return; } switch (msg.type) { case 'relay:code': this.pairingCode = msg.code; this._emit('code', msg.code); break; case 'relay:paired': this.sessionId = msg.sessionId; this._emit('paired', { sessionId: msg.sessionId }); break; case 'relay:message': this._emit('message', { sessionId: msg.sessionId, text: msg.text, timestamp: msg.timestamp }); break; case 'relay:client-disconnected': this.sessionId = null; this._emit('client-disconnected', { sessionId: msg.sessionId }); break; case 'relay:error': this._emit('error', { error: msg.error }); break; } }); ``` `bin/voice-bridge.js:111-123`: ```js relay.on('message', async ({ sessionId, text }) => { console.log(`[voice-bridge] User said: "${text}"`); relay.sendTyping(true); try { const response = await bridge.sendMessage(sessionId, text); console.log(`[voice-bridge] Agent response: "${response}"`); relay.sendMessage(response); relay.sendTyping(false); } catch (err) { console.error(`[voice-bridge] Error sending to agent:`, err.message); relay.sendMessage('Sorry, I encountered an error processing your message.'); relay.sendTyping(false); } }); ``` `lib/agent-bridge.js:18-35`: ```js return new Promise((resolve, reject) => { const args = [ 'agent', '--session-id', sessionId, '-m', text, ]; if (this.agent) { args.push('--agent', this.agent); } execFile('openclaw', args, ...[truncated 3019 chars]
Remediation
## Remediation Suggestions 1. Introduce an end-to-end pairing capability generated locally and exchanged through an authenticated pairing flow. Require every browser-originated command to carry a valid, unguessable capability. 2. Cryptographically authenticate messages independently of the relay, such as with signatures or a message authentication code based on a key known only to the paired endpoints. 3. Reject `relay:message` objects unless `msg.sessionId` strictly equals the currently active `this.sessionId`. 4. Validate message schemas before dispatch: - Require `sessionId` and `text` to be strings. - Enforce conservative maximum lengths. - Reject unknown fields and malformed identifiers. - Enforce expected state transitions so messages cannot be accepted before pairing. 5. Generate a dedicated local OpenClaw session rather than accepting a session identifier supplied by the relay. 6. Require explicit local approval before enabling agent tools or other sensitive capabilities for a newly paired client. 7. Run the agent with least privilege and disable filesystem, shell, credential, and network capabilities that are unnecessary for voice interaction. 8. Add replay protection using monotonic sequence numbers, timestamps, and unique nonces. 9. Record security-relevant events, including pairing changes and rejected session identifiers, without logging sensitive transcript contents.

T08 · Insecure Dependencies

Warning
Location
index.html:43
Finding
Installation Instructions Use Mutable Artifacts Without Integrity Verification## Vulnerability Details **File Location**: `index.html:43-44`, `package.json:21-23` **Vulnerability Type**: Unverified remote installation artifact and non-locked dependency resolution **Risk Level**: Medium ### Vulnerable Code `index.html:43-44`: ```html <pre>curl -sL https://hotbutter.ai/skill/download | tar -xzf - cd skill-hotbutter &amp;&amp; npm install</pre> ``` `package.json:21-23`: ```json "dependencies": { "ws": "^8.18.0" } ``` ### Technical Analysis The documented installation command downloads a mutable archive and streams it directly into `tar`. No immutable release version, expected checksum, or digital signature is supplied. Users therefore have no independent mechanism to establish that the downloaded package is the same package that was audited. The project also contains no lockfile in the audited directory, while `ws` is specified using a compatible-version range. Running `npm install` can consequently resolve a dependency version different from the one previously reviewed or tested. HTTPS protects the transfer against ordinary passive network modification, but it does not protect users if the distribution server, publishing account, certificate authority path, DNS environment, or upstream package release process is compromised. ### Attack Path 1. An attacker compromises the skill distribution endpoint, its deployment credentials, or the relevant dependency publishing channel. 2. The attacker replaces the mutable archive or causes the permitted dependency range to resolve to malicious content. 3. A user follows the documented `curl | tar` and `npm install` instructions. 4. The altered package is extracted without checksum or signature verification, or the altered dependency is installed without a reviewed lockfile. 5. Malicious JavaScript may execute through package lifecycle behavior or when the user starts `voice-bridge`. 6. The malicious code runs with the permissions of the in ...[truncated 508 chars]
Remediation
## Remediation Suggestions 1. Publish immutable, versioned release archives rather than relying only on a mutable download path. 2. Publish a SHA-256 or stronger digest for every release through an independently protected channel. 3. Prefer digitally signed release manifests or artifacts and document the exact signature-verification procedure. 4. Replace the streaming extraction instruction with separate download, verification, and extraction steps. For example: - Download the archive to a named file. - Verify its checksum or signature. - Inspect the archive paths. - Extract only after successful verification. 5. Commit an npm lockfile generated from reviewed dependencies and instruct users to install with `npm ci`. 6. Pin important production dependencies to reviewed versions and use automated dependency-update review rather than resolving new compatible versions implicitly. 7. Add continuous supply-chain checks, including lockfile integrity validation, dependency vulnerability scanning, provenance verification, and release reproducibility testing. 8. Protect package and distribution publishing accounts with multi-factor authentication, narrowly scoped tokens, and auditable release workflows.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (6)

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The script collects an email address and persists it to ~/.hotbutter without clearly disclosing retention, purpose, or file permissions. While this is not an exploit primitive by itself, it is a real privacy weakness because personal data is stored locally in plaintext and may be exposed to other local users, backups, or logs on shared systems.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
User speech text is forwarded to external components through the relay and agent bridge without an explicit warning at the point of use that message content leaves the local terminal session. In a voice interface, users may disclose sensitive information, so undisclosed transmission creates a meaningful privacy and data-handling risk even if the feature is intended.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code establishes a network connection to an external relay and immediately sends identifying data (`agentId` and `agentName`) on connection. There is no confirmation prompt, visible log/message, or explanatory comment/docstring in this file warning that data will be transmitted to `wss://hotbutter.ai`.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The HTML sets `lang="en"`, which establishes English as the page language. Under the policy rule, forcing a specific language without user opt-in can be a natural-language locale violation when no choice or justification is provided.

Unpinned Dependencies

Low
Category
Supply Chain
Content
}
  },
  "dependencies": {
    "ws": "^8.18.0"
  }
}
Confidence
95% confidence
Finding
The dependency uses a caret range (^8.18.0) rather than an exact pinned version, which makes builds non-reproducible and can cause different environments to install different releases over time. In a network-facing voice bridge that relays traffic and depends on WebSocket behavior, this increases supply-chain and stability risk, even though it is not by itself evidence of compromise.

Unverifiable Dependency: ws has 7 known advisory(ies) (CVE-2016-10518 (Remote Memory Disclosure in ws); CVE-2024-37890 (ws affected by a DoS when handling a request with many HTTP headers); CVE-2026-45736 (ws: Uninitialized memory disclosure) +4 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding
The manifest references ws without pinning an exact version, and ws has multiple known advisories across versions. Because this skill is explicitly a hosted voice relay using WebSockets, any vulnerable ws release could expose the service to denial of service, memory disclosure, or related network-triggered issues, making the uncertainty materially relevant in this context.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
lib/agent-bridge.js:29