T09 · Insecure Skill Coding Practices
- Location
scripts/tts.py:400- Finding
Batch TTS Output Path Traversal Permits Filesystem Writes Outside the Output Directory
- Content
View full analysis
Vulnerability Details
File Location:
scripts/tts.py:400-413, with the file-write sink inscripts/tts.py:327-335
Vulnerability Type: Unvalidated batch output path / path traversal
Risk Level: MediumVulnerable Code
python for i, text in enumerate(texts, 1): # Handle dict entries with custom voice/output if isinstance(text, dict): t = text.get("text", "") v = text.get("voice", voice_name) o = text.get("output", f"output_{i:04d}.mp3") else: t = str(text) v = voice_name o = f"output_{i:04d}.mp3" output_file = out_path / o print(f" [{i}/{len(texts)}] Processing: {t[:50]}...") if synthesize(t, v, str(output_file), voices_data, api_key, language, False, pronunciations): success += 1The resulting path reaches this write operation:
python with urllib.request.urlopen(req, timeout=30) as response: audio_data = response.read() # Write to file with open(output_path, "wb") as f: f.write(audio_data)Technical Analysis
The
outputproperty in a batch JSON entry is used as a filesystem path without checking whether it is absolute, contains parent-directory traversal components, or resolves outside the requested output directory.Joining
out_pathwith an absolute path can discard the intended base directory. Relative values such as../../target.mp3can similarly escape it after path resolution. The destination is opened withwb, so an existing user-writable file is truncated and replaced.The bytes written are audio returned by ElevenLabs rather than arbitrary executable content. Nevertheless, the operation violates least filesystem privilege because batch generation only requires writing inside the selected output directory.
Attack Path
- An attacker creates or modifies a TTS batch JSON file that a victim will process.
- The attacker supplies an output property such as:
json [ { "text": "At ...[truncated 1011 chars]- Remediation
View remediation
Remediation Suggestions
- Reject absolute output paths supplied by batch entries.
- Resolve the candidate path and verify that it remains beneath the resolved output directory:
python base_dir = Path(output_dir).resolve() base_dir.mkdir(parents=True, exist_ok=True) output_name = Path(o) if output_name.is_absolute(): raise ValueError("Absolute output paths are not permitted") output_file = (base_dir / output_name).resolve() if output_file != base_dir and base_dir not in output_file.parents: raise ValueError("Output path escapes the output directory")- If nested output folders are unnecessary, accept only a basename:
python output_file = base_dir / Path(o).name- Restrict allowed output extensions to expected audio formats.
- Create any permitted parent directories only after containment validation.
- Consider exclusive file creation or an explicit overwrite option so existing files are not silently truncated.
- Document that batch files should be treated as untrusted input and add tests covering absolute paths,
..traversal, and symbolic-link edge cases.
