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. ]]>
