Back to skill

Security audit

openclaw-voice

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent local voice-transcript CLI, but it needs review because it persists conversation data and publishes user-controlled text into shared agent-readable Markdown without clear safety boundaries.

Install only if you are comfortable with local retention of voice conversation data and with generated Markdown being readable by other agents in the workspace. Treat profile descriptions and conversation summaries as untrusted content, avoid putting secrets in transcripts or summaries, review or delete the SQLite database, backups, and interchange files as needed, and require separate review before enabling any future phone-calling implementation.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Findings (1)

T02 · Agent Memory Poisoning

Warning
Location
src/interchange.js:53
Finding
Persistent Indirect Prompt Injection Through Agent-Readable Interchange Markdown<![CDATA[ ## Vulnerability Details **File Location**: `src/interchange.js:53-72` and `src/interchange.js:84-97` **Vulnerability Type**: Persistent indirect prompt injection / agent memory poisoning **Risk Level**: Medium ### Vulnerable Code Profile names and descriptions are inserted directly into agent-readable Markdown: ```js function generateProfiles(dbOverride) { const db = dbOverride || getDb(); const profiles = listProfiles(db); let content = `# Voice Profiles `; profiles.forEach(p => { let desc = 'No description provided.'; try { const settings = JSON.parse(p.settings_json); desc = settings.description || desc; } catch {} content += `## ${p.name} ${desc} `; }); fs.writeFileSync(path.join(opsDir, 'profiles.md'), content); } ``` Conversation summaries are likewise inserted without escaping or trust-boundary markers: ```js function generateRecent(dbOverride) { const db = dbOverride || getDb(); const now = new Date(); now.setHours(0, 0, 0, 0); const todayStart = now.toISOString(); const recent = db.prepare(`SELECT id, summary, started FROM conversations WHERE ended IS NOT NULL ORDER BY ended DESC LIMIT 5`).all(); const todayCount = db.prepare(`SELECT COUNT(*) as count FROM conversations WHERE started >= ?`).get(todayStart).count; const durations = db.prepare(`SELECT (julianday(ended) - julianday(started)) * 86400 as duration FROM conversations WHERE ended IS NOT NULL`).all(); let totalDuration = 0; durations.forEach(d => { totalDuration += d.duration || 0; }); const totalMinutes = Math.round(totalDuration / 60); let content = `# Recent Voice Activity ## Last 5 Conversations `; recent.forEach(c => { content += `- ${c.id.substring(0, 8)}: ${c.summary || 'No summary'} (started ${c.started})\n`; }); content += ` ## Today's Conversation Count: ${todayCount} ## Total Conversation Duration: ${totalMinutes} minutes `; fs.writeFileSync(path.join(stateDir, 'recent.md'), content); } ` ...[truncated 3414 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Treat all database-derived content as untrusted** - Explicitly label summaries, profile names, and descriptions as user-provided data. - State in generated files that enclosed values must never be interpreted as instructions. 2. **Use structured serialization** - Prefer JSON with a fixed schema over free-form Markdown for inter-agent interchange. - Require consumers to parse specific data fields rather than ingesting the complete document as instructions. 3. **Apply context-aware encoding** - Escape Markdown headings, links, HTML, block quotes, code fences, and other structural characters before interpolation. - Normalize or reject control characters and bidirectional text controls. - Do not rely on HTML escaping alone when the consuming system is an AI agent. 4. **Separate trusted instructions from untrusted data** - Place user-controlled values inside clearly delimited data blocks. - Keep operational instructions in a separate trusted file that cannot be modified through profile or conversation inputs. - Configure consuming agents to treat interchange records exclusively as quoted evidence. 5. **Validate input** - Enforce reasonable maximum lengths for names, descriptions, and summaries. - Restrict profile names to a conservative character set. - Validate settings against an explicit JSON schema rather than accepting arbitrary objects. 6. **Harden downstream consumers** - Instruct consuming agents not to follow directives found in summaries, transcripts, profile metadata, or other user-controlled fields. - Require confirmation or policy checks before executing tools based on interchange content. 7. **Add security regression tests** - Test summaries and descriptions containing headings, links, HTML, code fences, and phrases such as `Ignore previous instructions`. - Verify that generated output preserves such values only as inert, clearly identified data. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (16)

Ae1

High
Category
analysis-evasion
Content
See src/cli.js for commands.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

High
Confidence
97% confidence
Finding
The spec states that phone conversations and transcripts are stored in the Voice DB, but it does not require any user-facing privacy disclosure, consent flow, retention policy, or access controls. Logging live phone conversations without clear notice is dangerous because it can expose highly sensitive personal data, create legal/compliance exposure, and expand the blast radius of any database compromise.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly promotes storing full transcript history, searchable past interactions, backup/restore, and publishing conversation data as Markdown, but it does not warn users that potentially sensitive voice content will be retained and exposed beyond the immediate session. In a voice/conversation tool, transcripts commonly contain personal, operational, or credential-like data, so undocumented retention and sharing behavior creates a real privacy and data-exposure risk.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The interchange section states that `voice refresh` generates Markdown summaries, transcript excerpts, and profile configurations that other agents can read, but it gives no warning that this broadens the trust boundary and may leak conversation data to additional consumers. Sharing conversation-derived artifacts with other agents materially increases exposure risk, especially when users may assume transcripts remain local to the voice tool.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill explicitly records audio, transcribes it, and stores transcripts, but the description provides no user-facing warning or consent notice about this data collection and retention. That creates a privacy risk because users may unknowingly disclose sensitive personal or credential information that is then persisted to disk.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill is designed to initiate outbound calls to a configured real phone number, yet the description lacks a prominent warning about placing real-world calls, associated costs, and the possibility of contacting an unintended recipient if the number is misconfigured. Because this action affects external parties outside the system boundary, missing warnings and confirmation controls make accidental or unauthorized harm more likely.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The CLI trigger descriptions are minimal and do not define who is authorized to start calls, what confirmation is required, or what safeguards prevent accidental or unauthorized invocation. In a skill that can place real outbound phone calls and incur charges, ambiguous activation semantics materially increase the risk of misuse, accidental dialing, and policy violations.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The restoreDb function recreates the target directory and writes backup contents into the target database path, which is a safety-critical operation because it can replace existing data. Although the docstring describes the parameters, there is no visible confirmation prompt, logging, or explicit warning that restore may overwrite the active database.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The `restore` command is an irreversible, safety-critical operation because it replaces the current database state, but the handler immediately invokes `restoreDb(file)` after only a generic description string. There is no confirmation prompt or stronger user-facing warning in this file to help prevent accidental data loss.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
This test is intended to prevent sensitive data leakage in generated interchange files, but the assertions do not actually validate that secret-like values are absent. `assert.notStrictEqual(profContent, /.../)` and the similar check on `recentContent` compare a string to a RegExp object, which will always pass, so the test can succeed even if voice identifiers or transcript content are written into output files. In a skill that exports operational/state markdown into a workspace, this weak test can mask real data exposure risks.

Known Vulnerable Dependency: uuid==10.0.0 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
85% confidence
Finding
The lockfile pins uuid to 10.0.0, and the referenced advisory indicates missing buffer bounds checks in certain UUID generation paths when a caller supplies a buf argument. This is a real supply-chain risk because vulnerable code is present in the dependency graph, though the practical exploitability depends on whether the application actually invokes affected v3/v5/v6 APIs with attacker-influenced buffer arguments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"refresh": "node src/interchange.js"
  },
  "dependencies": {
    "better-sqlite3": "^9.6.0",
    "commander": "^12.1.0",
    "uuid": "^10.0.0"
  },
Confidence
90% confidence
Finding
The dependency is specified with a caret range, which allows newer minor/patch versions to be installed over time. This can introduce supply-chain risk and reduce build reproducibility, especially for a CLI skill that may be installed in different environments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "better-sqlite3": "^9.6.0",
    "commander": "^12.1.0",
    "uuid": "^10.0.0"
  },
  "engines": {
Confidence
90% confidence
Finding
Using a caret version for commander permits automatic resolution to later releases within the major version, which weakens reproducibility and can pull in unexpected code changes. In a distributed Node.js package, this is a legitimate supply-chain hardening issue even if no immediate exploit is evident.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "better-sqlite3": "^9.6.0",
    "commander": "^12.1.0",
    "uuid": "^10.0.0"
  },
  "engines": {
    "node": ">=18"
Confidence
93% confidence
Finding
The uuid dependency is not pinned exactly, so installs may resolve to different versions over time. This increases supply-chain exposure and is more concerning here because the same dependency also has a reported advisory, making version control especially important.

Known Vulnerable Dependency: uuid==10.0.0 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
84% confidence
Finding
The listed uuid version is reported as affected by a missing buffer bounds check in certain v3/v5/v6 code paths when a buf argument is provided. If the skill or its transitive consumers invoke those APIs with attacker-influenced inputs, this could cause crashes or other denial-of-service behavior; however, from package.json alone there is no evidence of active exploitation or direct exposure, so impact remains limited.

Intent-Code Divergence

Low
Confidence
87% confidence
Finding
The comment says `getDb` is 'for internal use, but actually di...', implying a different or limited intent for the import, yet the imported symbol is never used anywhere in this file. This creates intent confusion in documentation because the comment describes a purpose that the code does not implement.

Static analysis

No suspicious patterns detected.