T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/stt_simple.py:39
- Finding
- Unvalidated Session Identifier Allows Transcript Writes Outside the Intended Output Directory## Vulnerability Details **File Location**: `scripts/stt_simple.py`, lines 39-55; attacker-controlled input originates at line 74 **Vulnerability Type**: Path traversal and arbitrary file placement **Risk Level**: High **Vulnerable Code**: ```python # Determine output directory based on session_id if session_id: # Use session-specific subdirectory for multi-Agent isolation output_dir = os.path.join(BASE_OUTPUT_DIR, session_id) else: # Default shared directory output_dir = BASE_OUTPUT_DIR os.makedirs(output_dir, exist_ok=True) # Generate unique filename with timestamp to avoid collisions base_name = Path(audio_path).stem timestamp = uuid.uuid4().hex[:8] output_txt = os.path.join(output_dir, f"{base_name}_{timestamp}.txt") with open(output_txt, "w", encoding="utf-8") as f: f.write(result["text"]) ``` The value used by this code is obtained directly from the command line: ```python session_id = sys.argv[4] if len(sys.argv) > 4 else None ``` ### Technical Analysis The `session_id` argument is used as a filesystem path component without validation or canonical-path containment checks. Python's `os.path.join()` does not guarantee that the resulting path remains beneath `BASE_OUTPUT_DIR`. A session identifier containing parent-directory segments, such as `../../other-directory`, can traverse outside the intended output directory. If `session_id` is an absolute path, `os.path.join(BASE_OUTPUT_DIR, session_id)` discards the base path entirely and returns the absolute path. The program subsequently calls `os.makedirs()` on the resulting path and writes the transcript to it. The randomized filename suffix limits deterministic replacement of a specific existing file, but it does not prevent arbitrary directory creation or placement of attacker-influenced transcript data in unintended filesystem locations. ### Attack Path 1. An attacker or untrusted caller supplies a valid audio f ...[truncated 1347 chars]
- Remediation
- ## Remediation Suggestions - Restrict `session_id` to a conservative identifier format, for example `^[A-Za-z0-9_-]{1,64}$`. - Reject absolute paths, path separators, `.` components, and `..` components. - Resolve both the base directory and candidate directory to canonical paths, then verify that the candidate remains beneath the base directory before creating it. - Use `pathlib.Path` containment checks rather than relying only on string-prefix comparisons. - Run the transcription process under a dedicated unprivileged account with write access limited to the transcript directory. - Set restrictive output permissions and consider per-session access controls because transcripts may contain sensitive information. - Add tests covering absolute paths, parent traversal, nested traversal, symlink traversal, empty identifiers, and excessively long identifiers. Example hardening approach: ```python import re from pathlib import Path base_dir = Path(BASE_OUTPUT_DIR).resolve() if session_id: if not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", session_id): raise ValueError("Invalid session identifier") output_dir = (base_dir / session_id).resolve() if output_dir.parent != base_dir: raise ValueError("Session directory escapes output root") else: output_dir = base_dir ```
