T09 · Insecure Skill Coding Practices
- Location
scripts/imagegen.py:397- Finding
API-Controlled Path Traversal Allows Filesystem Writes Outside the Output Directory
- Content
View full analysis
Vulnerability Details
File Location:
scripts/imagegen.py:397-414; duplicated inscripts/videogen.py:397-414
Vulnerability Type: Path traversal and arbitrary file overwrite
Risk Level: MediumVulnerable Code
python out_dir = Path(output_dir) out_dir.mkdir(parents=True, exist_ok=True) task_type = task.get("taskType", "task") for i, item in enumerate(succeeded): result_url = item.get("resultUrl") if not result_url: continue ext = ".mp4" if "video" in task_type.lower() else ".png" filename = f"{task_type}_{task_id}_{i+1}{ext}" out_path = out_dir / filename download_file(result_url, out_path) # Tail frame for item in succeeded: last_frame = item.get("lastFrameUrl") if last_frame: out_path = out_dir / f"{task_type}_{task_id}_lastframe.png" download_file(last_frame, out_path)The destination is subsequently opened in write-and-truncate mode:
python with open(str(out_path), "wb") as f: for chunk in resp.iter_content(chunk_size=8192): if chunk: f.write(chunk)Technical Analysis
The API response controls
taskType, whiletaskIdcan originate from an API generation response or command input. These values are interpolated directly into a filename without removing path separators, resolving the destination, or verifying that the resolved path remains underoutput_dir.pathlibdoes not neutralize traversal components when joining paths. For example, ataskTypecontaining../../destinationproduces a path such as:text /configured/output/../../destination_task-id_1.pngThe download routine then opens the path with
"wb", which creates a missing file or truncates an existing file. The same vulnerable implementation exists in both generation clients.Attack Path
- A user invokes image or video generation and the client polls the configured API. 2 ...[truncated 1109 chars]
- Remediation
View remediation
Remediation Suggestions
-
Do not use API-controlled values as local filenames. Generate filenames locally using a UUID or validated numeric index.
-
If identifiers must be retained, restrict them to a conservative allowlist such as
[A-Za-z0-9_-]. -
Resolve and validate every destination before opening it:
python import re from pathlib import Path safe_task_type = re.sub(r"[^A-Za-z0-9_-]", "_", str(task_type)) safe_task_id = re.sub(r"[^A-Za-z0-9_-]", "_", str(task_id)) root = Path(output_dir).expanduser().resolve() root.mkdir(parents=True, exist_ok=True) destination = (root / f"{safe_task_type}_{safe_task_id}_{i + 1}{ext}").resolve() if not destination.is_relative_to(root): raise SystemExit("Refusing to write outside the output directory") -
Download into a temporary file inside the validated directory and atomically rename it after validation.
-
Avoid silent truncation. Use exclusive creation or require explicit permission before replacing an existing file.
-
Apply the same correction to
scripts/videogen.py.
-
