Back to skill

Security audit

VoiceClaw

Security checks for vulnerabilities and agentic risk

Overview

VoiceClaw is a coherent local voice transcription and speech skill, with normal setup risks and a temp-file hardening issue but no evidence of hidden exfiltration, persistence, or unsafe agent override behavior.

Install only if you are comfortable managing local Whisper, Piper, ffmpeg, and model files yourself. Prefer pinned, checksum-verified model downloads, run the skill as an unprivileged user, and update the temp-file handling to use mktemp before using it on shared machines.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/transcribe.sh:16
Finding
Predictable Temporary File Paths Permit Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/transcribe.sh:16,36-40`; `scripts/speak.sh:16-20,50-52`; `SKILL.md:135-137` **Vulnerability Type**: Predictable temporary files and unsafe file creation **Risk Level**: Medium ### Vulnerable Code `scripts/transcribe.sh:16,36-40`: ```bash TMP_WAV="/tmp/voiceclaw_stt_$$.wav" cleanup() { rm -f "$TMP_WAV"; } trap cleanup EXIT # Convert to 16kHz mono WAV (Whisper requirement) — local ffmpeg, no network ffmpeg -i "$AUDIO_FILE" -ar 16000 -ac 1 "$TMP_WAV" -y -loglevel error ``` `scripts/speak.sh:16-20,50-52`: ```bash TEXT="${1:-}" OUTPUT="${2:-/tmp/voiceclaw_tts_$$.wav}" VOICE="${3:-en_US-lessac-medium}" VOICES_DIR="${VOICECLAW_VOICES_DIR:-$HOME/.local/share/piper/voices}" PIPER_BIN="${PIPER_BIN:-$(which piper 2>/dev/null || echo piper)}" echo "$TEXT" | "$PIPER_BIN" -m "$MODEL" "${CONFIG_ARGS[@]}" -f "$OUTPUT" 2>/dev/null echo "$OUTPUT" ``` `SKILL.md:135-137`: ```bash RESPONSE="Deployment complete. All checks passed." WAV=$(bash path/to/voiceclaw/scripts/speak.sh "$RESPONSE" /tmp/reply_$$.wav) ffmpeg -i "$WAV" -c:a libopus -b:a 32k /tmp/reply_$$.ogg -y -loglevel error ``` ### Technical Analysis The scripts and documented integration example construct files directly in the shared `/tmp` directory using the process ID (`$$`). Process IDs are observable or predictable, and these paths are not reserved through an atomic, exclusive file-creation operation. A local attacker can create a symbolic link at the expected path before the victim process writes to it. The transcription command explicitly uses `ffmpeg -y`, allowing replacement of an existing destination. Depending on the behavior of Piper and `ffmpeg`, the write can follow the symbolic link and modify its target. The transcription cleanup trap removes the predictable path but does not prevent the race or verify that the path is a regular file owned by the current process. The speech script does not clean up its default output at all because ...[truncated 1408 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create an unpredictable private temporary directory with `mktemp -d`: ```bash TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/voiceclaw.XXXXXXXX")" chmod 700 "$TMP_DIR" TMP_WAV="$TMP_DIR/input.wav" trap 'rm -rf -- "$TMP_DIR"' EXIT ``` 2. Store every intermediate WAV or OGG file inside that private directory rather than directly under `/tmp`. 3. Do not use PID-derived names as the only uniqueness mechanism. 4. Avoid relying solely on a pre-write symbolic-link check, because checking and writing separately introduces a time-of-check/time-of-use race. 5. For caller-provided output paths, document that the caller must select a trusted directory. Where practical, reject symbolic links and require the parent directory to be owned by the current user and not writable by untrusted users. 6. Remove PID-based paths from the integration examples in `SKILL.md` and demonstrate `mktemp` instead. 7. Run the Skill as an unprivileged service account to limit the files that could be affected if another file-handling flaw occurs. ]]>

T08 · Insecure Dependencies

Note
Location
README.md:35
Finding
Whisper Model Download Is Not Pinned or Integrity-Verified<![CDATA[ ## Vulnerability Details **File Location**: `README.md:35-42` **Vulnerability Type**: Unverified third-party model dependency **Risk Level**: Low ### Vulnerable Code ```bash - `whisper` — whisper.cpp binary ([install guide](https://github.com/ggerganov/whisper.cpp)) - Whisper model: `ggml-base.en.bin` — auto-downloaded on first use, or manually: ```bash # One-time setup only — not run by the skill scripts mkdir -p ~/.cache/whisper curl -L -o ~/.cache/whisper/ggml-base.en.bin \ https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin ``` ``` ### Technical Analysis The documented setup retrieves the model from the mutable `main` reference and installs it without checking a cryptographic checksum or trusted signature. TLS protects data in transit but does not establish that the downloaded file is the exact artifact reviewed or expected by the project. If the upstream account, repository, release process, or hosting infrastructure is compromised, users could receive a replaced model. An unintentionally corrupted download would likewise remain undetected. The model is data rather than a directly executed shell payload. Nevertheless, it controls transcription behavior and is parsed by native Whisper software. A malicious model could manipulate transcription results, while a parser vulnerability in the installed Whisper implementation could increase the impact of processing a crafted file. ### Attack Path 1. An attacker compromises the upstream model repository, its publishing credentials, or another component capable of changing the object resolved by the `main` URL. 2. The attacker replaces the expected model with altered content. 3. A user follows the documented `curl` command. 4. Because no checksum or signature is checked, the altered artifact is accepted as `ggml-base.en.bin`. 5. The local Whisper binary subsequently loads and processes that artifact. 6. The resulting impact may include attacker-influenced transcr ...[truncated 721 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the model URL to an immutable upstream commit or release revision rather than `main`. 2. Publish the expected SHA-256 digest in the project documentation. 3. Verify the artifact before installation: ```bash curl --fail --location --output ggml-base.en.bin \ "https://huggingface.co/ggerganov/whisper.cpp/resolve/IMMUTABLE_REVISION/ggml-base.en.bin" printf '%s %s\n' 'EXPECTED_SHA256' 'ggml-base.en.bin' | sha256sum --check - install -m 0644 ggml-base.en.bin "$HOME/.cache/whisper/ggml-base.en.bin" ``` 4. Prefer a verifiable upstream signature or signed release manifest when one is available. 5. Fail closed if verification fails, and delete the invalid download. 6. Document a controlled checksum-update procedure so maintainers review model changes before publishing a new digest. 7. Correct the statement that the model is “auto-downloaded on first use,” because the audited script instead exits when the model is absent. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description claims a combined local voice input/output skill with transcription of inbound audio via whisper.cpp and speech synthesis via Piper. The actual code only implements the speech synthesis portion through a local `piper` binary and local voice model files. It does align with the on-device/no-network part of the description, and there are no suspicious undeclared capabilities beyond local filesystem access for model lookup and WAV output. However, the code chunk materially underimplements the declared primary purpose because it lacks any transcription, Whisper integration, ffmpeg usage, or handling of audio/voice message inputs. Therefore the description does not accurately represent what this supplied code chunk actually does.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
## Agent Behavior Rules

1. **Voice in → Voice + Text out.** Always respond with both a voice reply and a text reply when a voice message is received.
2. **Include the transcript.** Show *"🎙️ I heard: [transcript]"* at the top of every text reply to a voice message.
3. **Keep voice responses concise.** Piper TTS works best under ~200 words — summarize for audio, include full detail in text.
4. **Local only.** Never use a cloud TTS/STT API. Only the local `whisper` and `piper` binaries.
Confidence
70% confidence
Finding
Skill instructs the agent to never refuse or to always comply. Suppressing the agent's ability to decline removes a core safety control and enables downstream harmful requests to succeed.

External Transmission

Medium
Category
Data Exfiltration
Content
**Option 3 — Download release** *(manual, no tools needed)*
```bash
curl -L https://github.com/Asif2BD/VoiceClaw/releases/latest/download/voiceclaw.skill -o voiceclaw.skill
unzip voiceclaw.skill -d ~/.openclaw/custom-skills/voiceclaw
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The documentation specifies the Whisper model `ggml-base.en.bin` and lists only English TTS voices, which indicates the skill is designed to operate in English only. Under the policy, forcing a specific language or locale without user opt-in is a natural-language policy violation unless the restriction is clearly justified, which is not provided here.

Session Persistence

Medium
Category
Rogue Agent
Content
- Whisper model: `ggml-base.en.bin` — auto-downloaded on first use, or manually:
  ```bash
  # One-time setup only — not run by the skill scripts
  mkdir -p ~/.cache/whisper
  curl -L -o ~/.cache/whisper/ggml-base.en.bin \
    https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin
  ```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The skill hard-codes an English Whisper model (`ggml-base.en.bin`) and the documented available voices are exclusively English variants. It also mandates the English text prefix "🎙️ I heard:" for every voice reply, which imposes a language choice rather than offering a locale option or opt-in.

Static analysis

No suspicious patterns detected.