T09 · Insecure Skill Coding Practices
- Location
references/setup.md:112- Finding
Server-Side URL Fetching Permits SSRF by Default
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a coherent speech-service skill, but its documented defaults can expose sensitive audio, files, and server-side network access without enough containment.
Install only for a trusted self-hosted Talkies instance. Bind to localhost or a protected network, set TALKIES_AUTH_TOKEN, enable TALKIES_BLOCK_PRIVATE_DOWNLOADS before accepting untrusted callers, avoid staging sensitive media on shared instances, clean up staged files and custom voices, and prefer pinned image digests over latest tags.
references/setup.md:112Server-Side URL Fetching Permits SSRF by Default
references/setup.md:16Unauthenticated Service Is Published on All Host Interfaces by Default
SKILL.md:489Persistent Shared File Staging Allows Cross-Caller Disclosure and Deletion
references/setup.md:16Installation Runs Mutable Third-Party Container Image Tags
The skill documents a destructive administrative endpoint that unloads models from memory, and states it may be unauthenticated when TALKIES_AUTH_TOKEN is unset. An agent invoking this endpoint with attacker-influenced model IDs or without explicit user approval can disrupt in-flight work and cause denial of service via forced cold reloads.
| `GET /healthz` | Unauthenticated liveness. Returns `{ok, device, models}`. |
| `GET /v1/models` | OpenAI-style list of configured slugs. Each entry includes a `modality` field (`asr` or `tts`) so clients can filter. |
| `GET /api/ps` | Currently-loaded models with per-model `idle_seconds`. |
| `DELETE /api/ps/{model_id}` | Evict one model from memory. Slug can be URL-encoded (`/` → `%2F`). 404 if not loaded. |
| `POST /unload` | Evict every loaded model. Returns the list actually unloaded. |
Model eviction (`DELETE /api/ps/...`, `POST /unload`) forces a cold-load for anyone mid-request, so only do it for explicit maintenance the user asked for (e.g. "free up VRAM"). Auth is only enforced when `TALKIES_AUTH_TOKEN` is set — require it on shared deployments and don't expose these routes on an unauthenticated network.
This section explicitly notes that DELETE /api/ps and POST /unload can affect other users and may be exposed without authentication. That creates a realistic misuse path where an agent or unauthenticated caller can repeatedly evict models, degrading availability and interfering with concurrent workloads.
| `DELETE /api/ps/{model_id}` | Evict one model from memory. Slug can be URL-encoded (`/` → `%2F`). 404 if not loaded. |
| `POST /unload` | Evict every loaded model. Returns the list actually unloaded. |
Model eviction (`DELETE /api/ps/...`, `POST /unload`) forces a cold-load for anyone mid-request, so only do it for explicit maintenance the user asked for (e.g. "free up VRAM"). Auth is only enforced when `TALKIES_AUTH_TOKEN` is set — require it on shared deployments and don't expose these routes on an unauthenticated network.
Behind these: an **idle sweeper** runs every `TALKIES_SWEEPER_INTERVAL` s (default 60) and unloads anything not used in `TALKIES_MODEL_TTL` s (default 600). Set `TALKIES_MODEL_TTL=0` to disable.
The documented /v1/files API allows listing, reading, and deleting files in a shared unisolated namespace with no per-caller ownership. Even though the skill warns against browsing or deleting others' files, an agent with access could still be induced to enumerate or remove unrelated users' staged media, causing both confidentiality loss and destructive tampering.
| GET /v1/files | List every staged file. Returns {"files": [{"path", "size", "modified"}]}. Enumerable by anyone with API access — no per-file ownership/isolation. |
| PUT /v1/files/{path} | Upload raw bytes (--data-binary @local-file). Capped at TALKIES_MAX_UPLOAD_BYTES. Atomic write (.part → rename). |
| GET /v1/files/{path} | Streams file back. Content-Type guessed by extension. 404 if missing. |
| DELETE /v1/files/{path} | Removes a staged file and prunes empty parent dirs (404 if missing). Files don't self-expire, so call this to clean up when done. On a shared bucket every path is listable by any caller, so only remove paths you staged yourself. |
# Stage once.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Discover TTS voices, then synthesize an MP3.
curl -s $TALKIES_URL/v1/audio/voices | jq
curl -s $TALKIES_URL/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro-82m",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Discover TTS voices, then synthesize an MP3.
curl -s $TALKIES_URL/v1/audio/voices | jq
curl -s $TALKIES_URL/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro-82m",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Discover TTS voices, then synthesize an MP3.
curl -s $TALKIES_URL/v1/audio/voices | jq
curl -s $TALKIES_URL/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro-82m",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Discover TTS voices, then synthesize an MP3.
curl -s $TALKIES_URL/v1/audio/voices | jq
curl -s $TALKIES_URL/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro-82m",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Discover TTS voices, then synthesize an MP3.
curl -s $TALKIES_URL/v1/audio/voices | jq
curl -s $TALKIES_URL/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "kokoro-82m",
Uploading media to /v1/files causes local file contents to leave the host and persist server-side until deletion. In this skill, staged files are also globally enumerable and readable to any API caller on the same Talkies instance, which materially increases confidentiality risk beyond ordinary upload behavior.
# Stage once.
curl -X PUT --data-binary @lecture.mp3 \
-H "Content-Type: audio/mpeg" \
$TALKIES_URL/v1/files/lectures/2026-03-15/lecture.mp3
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# tools/list
curl -s $TALKIES_URL/v1/mcp/ \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
ffmpeg -i video.mp4 -vn -acodec libmp3lame audio.mp3
curl -s $TALKIES_URL/v1/audio/transcriptions \
-F "file=@audio.mp3" \
-F "model=whisper-large-v3" \
-F "response_format=srt" > video.srt
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Docker image references without a specific tag (:latest is implicit) or digest (@sha256:...) can be silently replaced by a malicious image.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
## Qwen3-TTS Custom Voices
**Acceptable use:** only supply reference voice samples you're authorized to process, and only with the speaker's informed consent. Voice cloning reproduces someone's actual timbre/prosody — never use it to impersonate a real person without consent, or for fraud, deception, or any form of unauthorized voice replication.
`qwen3-tts-0.6b` is a voice-cloning TTS — it takes a reference `.wav` and clones the speaker's timbre / prosody onto whatever text you supply. The voice catalog is built from two on-disk dirs that are merged at request time (live, no restart):
The documentation directs users to place custom voice samples and transcripts in a persistent host-mounted directory, and elsewhere states that data under /data persists across restarts. Persistent storage of biometric voice samples and associated transcripts increases privacy risk if the host is shared, backups are broad, or old reference material is not cleaned up; in this skill's context, the risk is elevated because voice-cloning data is especially sensitive.
Use a custom voice:
mkdir -p $HOME/talkies-data/custom-voices/team-a
cp jane-reading.wav $HOME/talkies-data/custom-voices/team-a/jane.wav
echo "And the silken sad uncertain rustling of each purple curtain." \
> $HOME/talkies-data/custom-voices/team-a/jane.txt
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
echo "And the silken sad uncertain rustling of each purple curtain." \
> $HOME/talkies-data/custom-voices/team-a/jane.txt
curl -s http://localhost:8000/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-tts-0.6b",
The documentation instructs users to docker pull psyb0t/talkies:latest, which relies on a mutable tag. Mutable tags can be retargeted by a publisher or compromised registry account, causing users to fetch unexpected code and weakening reproducibility and supply-chain integrity.
No suspicious patterns detected.