T09 · Insecure Skill Coding Practices
- Location
scripts/edit_tool.py:1468- Finding
Project-controlled output symlinks can redirect rendering writes outside the project
- Content
View full analysis
Vulnerability Details
File Location:
scripts/edit_tool.py, lines 1468–1470, 1493, 1569–1572, 1583–1587, and 2218–2222
Vulnerability Type: Improper link resolution and unrestricted output redirection
Risk Level: MediumComplete Code Snippet
python output_root = episode / OUTPUT_DIRECTORY segments_root = output_root / SEGMENT_DIRECTORY segments_root.mkdir(parents=True, exist_ok=True)python segment = segments_root / f"{cut.cut_id}.mp4"python if cues: subtitle_path = output_root / "字幕.srt" subtitle_path.write_text(_build_srt(cues), encoding="utf-8")python if cues and renderer != "remotion": styled = styled_path = output_root / "字幕.ass" styled.write_text( _build_ass(cues, canvas["width"] or 1080, canvas["height"] or 1920), encoding="utf-8", )python props = output_root / "叠层.props.json" props.write_text( json.dumps(_overlay_props(cues, layers, canvas, duration), ensure_ascii=False, indent=2), encoding="utf-8", )Other rendering destinations, including segment and final media files, are passed to ffmpeg with overwrite mode enabled:
python command = [ffmpeg, "-hide_banner", "-loglevel", "error", "-y", "-i", str(joined)]Technical Analysis
The renderer derives its output directory directly from the selected episode:
python output_root = episode / OUTPUT_DIRECTORYIt then creates or reuses that path without checking whether the output directory, an intermediate component, or an individual output file is a symbolic link.
Path.mkdir(..., exist_ok=True)accepts an existing symlink to a directory, andPath.write_text()follows an existing destination symlink.The same trust issue affects files written by ffmpeg. Output destinations are supplied with
-y, allowing existing destinations to be overwritten, but there is no preceding no-follow or resolved-pat ...[truncated 2094 chars]- Remediation
View remediation
Remediation Suggestions
- Resolve the project root, episode directory, and intended output root before rendering. Reject execution unless the episode and resolved output destination remain within the authorized project root.
- Reject symbolic links in every component of
制作成果/成片, including the output directory, segment directory, and existing destination files. - Before every write, verify both the parent directory and destination using descriptor-based, no-follow operations rather than relying only on
Path.resolve()checks that can be invalidated by races. - For text outputs, open files with platform-supported protections such as
os.open()withO_NOFOLLOW, restrictive permissions, and safe creation flags. Write to a securely created temporary file in the validated directory and atomically replace the destination. - Before invoking ffmpeg, require the destination parent to be a validated real directory and reject any existing destination that is a symlink or non-regular file. Prefer rendering to securely created temporary paths followed by an atomic rename.
- Apply a strict filename-only grammar to
cut_idbefore using it in segment filenames, disallowing slashes, path separators, dot components, and control characters. - Add regression tests covering a symlinked output root, symlinked segment directory, and symlinked subtitle, properties, and final-media destinations.
