Back to skill

Security audit

WebChat Voice Proxy

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for adding voice input, but it has real security weaknesses around microphone audio, gateway token handling, LAN exposure, and persistent UI modification that deserve review before installation.

Install only after reviewing the scripts and preferably use the newer split replacement skills. Keep the proxy bound to 127.0.0.1, avoid custom HTTPS ports unless the endpoint bug is fixed, do not expose it on a LAN without reliable authentication, and be aware that continuous mode can send transcribed speech into chat automatically after a short review period.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
assets/https-server.py:57
Finding

Transcription Authentication Fails Open When Gateway Configuration Cannot Be Loaded

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
assets/voice-input.js:28
Finding

Custom HTTPS Ports Can Send Audio and the Gateway Token to a Client-Side Loopback Service

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/uninstall.sh:48
Finding

Uninstaller Removes Unrelated Gateway Allowed Origins by Port Substring

Content
View full analysis
`. For port 8443, this removes unrelated entries such as: - `https://another-service.example:8443` - `https://admin.example:84430` - Any other origin containing the same substring The operation is destructive because the modified JSON is immediately written back and the gateway is subsequently restarted. ### Attack Path 1. The gateway configuration contains the origin installed by this Skill and one or more unrelated legitimate origins using the same port. 2. The user runs `scripts/uninstall.sh`. 3. The list comprehension removes every entry containing the selected port substring. 4. The script writes the reduced configuration to disk. 5. The gateway is restarted. 6. Users relying on the unrelated origins can no longer access the Control UI through those origins. An attacker who can influence ...[truncated 604 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (32)

Tainted flow: 'req' from os.environ.get (line 93, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

VOICE_TRANSCRIBE_URL is taken from the environment and used directly in urllib.request.urlopen, allowing the service to forward arbitrary request bodies to any configured destination. In environments where attackers can influence configuration, this becomes an SSRF-style primitive and can exfiltrate microphone audio or probe internal network services; the risk is amplified because the proxy may intentionally be exposed beyond localhost.

Content

Scanner excerpt · assets/https-server.py (reported line 101)May include surrounding context.

python
loop = asyncio.get_event_loop()
        data = await loop.run_in_executor(
            None,
            lambda: urllib.request.urlopen(req, timeout=120).read(),
        )
        return web.Response(
            body=data,

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · SECURITY-AUDIT.md (reported line 70)May include surrounding context.

md
| 4.1 | `https-server.py` | **Path traversal protection**: `os.path.normpath()` + prefix check ensures static file serving stays within `CONTROL_UI` dir | Info | ✅ OK |
| 4.2 | `https-server.py` | Returns `HTTPForbidden` for traversal attempts | Info | ✅ OK |
| 4.3 | `deploy.sh` | Writes only to user-owned dirs (`~/.openclaw/`, `~/.config/systemd/user/`, npm global) | Info | ✅ OK |
| 4.4 | `deploy.sh` | No `chmod 777`, no world-writable files | Info | ✅ OK |
| 4.5 | `https-server.py` | TLS key now gets explicit `os.chmod(key_path, 0o600)` after cert generation | **Low** | ✅ Fixed |

**Fixed (4.5):** Explicit `os.chmod(key_path, 0o600)` is now applied after cert generation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill’s declared purpose understates that it installs a persistent startup hook and performs UI patch/injection on gateway startup. Hidden or under-declared persistence and injection behavior is dangerous because it survives updates and continues modifying the interface automatically, which increases the chance of unnoticed long-term tampering.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill’s declared purpose understates that it installs a persistent startup hook and performs UI patch/injection on gateway startup. Hidden or under-declared persistence and injection behavior is dangerous because it survives updates and continues modifying the interface automatically, which increases the chance of unnoticed long-term tampering.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/uninstall.sh (reported line 20)May include surrounding context.

sh
echo "[1/5] Stopping and removing openclaw-voice-https.service..."
systemctl --user stop openclaw-voice-https.service 2>/dev/null || true
systemctl --user disable openclaw-voice-https.service 2>/dev/null || true
rm -f "$HOME/.config/systemd/user/openclaw-voice-https.service"
systemctl --user daemon-reload 2>/dev/null || true
echo "      done."

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · scripts/uninstall.sh (reported line 36)May include surrounding context.

sh
# 3) Remove voice-input.js from Control UI and undo index.html injection
echo "[3/5] Reverting Control UI..."
if [[ -f "$ASSET_DIR/voice-input.js" ]]; then
  rm -f "$ASSET_DIR/voice-input.js"
  echo "      removed: voice-input.js asset"
fi

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

The script deletes files under $VOICE_DIR, which is derived from the WORKSPACE environment variable, without validating that the resolved path is the intended application workspace. If an attacker or confused operator sets WORKSPACE to an unexpected location, the uninstall script could remove arbitrary matching files and potentially rmdir that directory if it becomes empty.

Content

Scanner excerpt · scripts/uninstall.sh (reported line 88)May include surrounding context.

sh
# 7) Remove workspace runtime files (voice-input.js, https-server.py, i18n.json)
echo "[7/7] Removing workspace runtime files..."
for f in voice-input.js https-server.py i18n.json; do
  rm -f "$VOICE_DIR/$f"
done
# Remove dir if empty
rmdir "$VOICE_DIR" 2>/dev/null && echo "      removed: $VOICE_DIR" || echo "      $VOICE_DIR not empty, kept."

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SECURITY-AUDIT.md (reported line 70)May include surrounding context.

md
| 4.1 | `https-server.py` | **Path traversal protection**: `os.path.normpath()` + prefix check ensures static file serving stays within `CONTROL_UI` dir | Info | ✅ OK |
| 4.2 | `https-server.py` | Returns `HTTPForbidden` for traversal attempts | Info | ✅ OK |
| 4.3 | `deploy.sh` | Writes only to user-owned dirs (`~/.openclaw/`, `~/.config/systemd/user/`, npm global) | Info | ✅ OK |
| 4.4 | `deploy.sh` | No `chmod 777`, no world-writable files | Info | ✅ OK |
| 4.5 | `https-server.py` | TLS key now gets explicit `os.chmod(key_path, 0o600)` after cert generation | **Low** | ✅ Fixed |

**Fixed (4.5):** Explicit `os.chmod(key_path, 0o600)` is now applied after cert generation.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · SECURITY-AUDIT.md (reported line 70)May include surrounding context.

md
| 4.1 | `https-server.py` | **Path traversal protection**: `os.path.normpath()` + prefix check ensures static file serving stays within `CONTROL_UI` dir | Info | ✅ OK |
| 4.2 | `https-server.py` | Returns `HTTPForbidden` for traversal attempts | Info | ✅ OK |
| 4.3 | `deploy.sh` | Writes only to user-owned dirs (`~/.openclaw/`, `~/.config/systemd/user/`, npm global) | Info | ✅ OK |
| 4.4 | `deploy.sh` | No `chmod 777`, no world-writable files | Info | ✅ OK |
| 4.5 | `https-server.py` | TLS key now gets explicit `os.chmod(key_path, 0o600)` after cert generation | **Low** | ✅ Fixed |

**Fixed (4.5):** Explicit `os.chmod(key_path, 0o600)` is now applied after cert generation.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SECURITY-AUDIT.md (reported line 133)May include surrounding context.

md
### All Medium/Low Issues Resolved

1. **Auth on `/transcribe` (3.2/3.3)** — ✅ Fixed. Bearer token auth matching gateway token.
2. **TLS key permissions (4.5)** — ✅ Fixed. Explicit `chmod 600` after generation.
3. **aiohttp version (8.1)** — ✅ Fixed. Minimum `>=3.9.0` documented and enforced.

### Remaining Recommendations

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding

The skill declares substantial capabilities—file modification, shell execution, network access, environment use, and persistence via systemd/startup hooks—without an explicit tool-scope or permissions declaration. That omission weakens reviewability and informed consent, making it easier for users to run a skill that performs broader actions than expected.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 30)May include surrounding context.

md
persistence:
    - "User systemd service: openclaw-voice-https.service (HTTPS/WSS proxy)"
    - "Gateway startup hook: voice-input-inject (re-injects JS after updates)"
  privileges: user-level only, no root/sudo required
  dependencies:
    - python3 with aiohttp >= 3.9.0 (pip)
    - faster-whisper transcription service on port 18790

Session Persistence

Medium
Category
Rogue Agent
Confidence
96% confidence
Finding

The skill installs a user systemd service that starts on boot and a hook that re-injects UI changes after updates, creating durable persistence. Persistence is not inherently malicious, but in combination with script injection and network proxying it increases the blast radius of mistakes or compromise by keeping the behavior active without repeated user action.

Content

Scanner excerpt · SKILL.md (reported line 81)May include surrounding context.

md
- **Localhost by default**: The HTTPS proxy binds to `127.0.0.1` only. It is **not** reachable from other devices on your network unless you explicitly set `VOICE_HOST` to a LAN IP.
- **LAN exposure**: Setting `VOICE_HOST=<LAN-IP>` exposes the proxy (and by extension the gateway WebSocket and transcription endpoint) to all devices on that network. Only do this on trusted networks.
- **Persistence**: This skill installs a **user systemd service** (`openclaw-voice-https.service`) that starts automatically on boot, and a **gateway hook** that re-injects the UI script after updates. Use `uninstall.sh` to fully revert.
- **Self-signed TLS**: The auto-generated certificate is not trusted by browsers. You will see a certificate warning on first access.

## Deploy

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

This documented creation and enabling of a persistent HTTPS proxy service confirms long-lived background execution. In this skill’s context, persistence is more sensitive because the service fronts gateway WebSocket access and transcription functionality, and can optionally expose them to a LAN if configured broadly.

Content

Scanner excerpt · SKILL.md (reported line 132)May include surrounding context.

md
| Control UI HTML | `<npm-global>/openclaw/dist/control-ui/index.html` | Adds `<script>` tag for voice-input.js |
| Control UI asset | `<npm-global>/openclaw/dist/control-ui/assets/voice-input.js` | Copies mic button JS |
| Gateway config | `~/.openclaw/openclaw.json` | Adds HTTPS origin to `gateway.controlUi.allowedOrigins` |
| Systemd service | `~/.config/systemd/user/openclaw-voice-https.service` | Creates + enables persistent HTTPS proxy |
| Gateway hook | `~/.openclaw/hooks/voice-input-inject/` | Installs startup hook that re-injects JS after updates |
| Workspace files | `~/.openclaw/workspace/voice-input/` | Copies voice-input.js, https-server.py |
| TLS certs | `~/.openclaw/workspace/voice-input/certs/` | Auto-generated self-signed cert on first run |

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The live transcription control states that captured speech is auto-sent after a short review period, but the description does not foreground this as a privacy-sensitive behavior. Users may reasonably expect transcription preview without automatic submission, leading to accidental disclosure of sensitive spoken content into chat workflows.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · assets/https-server.py (reported line 38)May include surrounding context.

python
return explicit
    try:
        # Safe: fixed argument list, no user input, no shell=True
        npm_root = subprocess.check_output(["npm", "-g", "root"], text=True, stderr=subprocess.DEVNULL).strip()
        candidate = os.path.join(npm_root, "openclaw", "dist", "control-ui")
        if os.path.isdir(candidate):
            return candidate

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · assets/https-server.py (reported line 240)May include surrounding context.

python
return

    # Safe: fixed argument list, no user input, no shell=True
    subprocess.run([
        "openssl", "req", "-x509", "-nodes", "-newkey", "rsa:2048",
        "-keyout", str(key_path),
        "-out", str(cert_path),

Tainted flow: 'key_path' from os.environ.get (line 233, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · assets/https-server.py (reported line 240)May include surrounding context.

python
return

    # Safe: fixed argument list, no user input, no shell=True
    subprocess.run([
        "openssl", "req", "-x509", "-nodes", "-newkey", "rsa:2048",
        "-keyout", str(key_path),
        "-out", str(cert_path),

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · assets/https-server.py (reported line 248)May include surrounding context.

python
"-subj", "/CN=openclaw-voice-local",
    ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

    # SECURITY: Ensure private key is not group/world-readable
    os.chmod(str(key_path), 0o600)

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code reads a bearer token from localStorage and reuses it for transcription requests, extending the token's scope to a secondary endpoint without clear need or user awareness. This increases blast radius if the transcription service is compromised, misconfigured, or reachable in an unexpected context, and localStorage storage also makes the token broadly accessible to any script running in the page.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill captures microphone audio and sends it to a transcription endpoint with fetch(), but the code provides no explicit consent or disclosure that recorded audio leaves the browser. Users may assume audio is processed locally or only used transiently for UI behavior, making this a meaningful privacy and transparency issue.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Continuous mode aggregates ongoing speech, periodically transcribes it, and by default progresses toward automatic submission unless interrupted. This creates a compounded privacy and integrity risk because larger amounts of potentially sensitive speech are collected and transformed into outbound chat content with limited user control.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill goes beyond a passive voice UI by programmatically inserting transcribed content into the chat textarea and automatically sending messages via handleSendChat(). In continuous mode it can submit speech-derived content after a short timeout, which creates an integrity and privacy risk because users may not realize spoken audio will become outbound chat messages without an explicit confirmation step.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Continuous transcription mode auto-sends recognized text after a short review timer unless the user interacts with the textarea. That behavior can transmit unintended, misrecognized, or sensitive content without a clear warning, especially in a voice feature where recognition errors are expected.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The startup hook unconditionally executes a host shell script via bash during gateway startup, which creates a privileged code-execution path with side effects outside the TypeScript runtime. Although the script path is not user-controlled and shell interpolation is avoided, this still expands the skill's trust boundary and allows any compromise or unexpected change to inject.sh to run arbitrary commands on the host.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
hooks/handler.ts:14