T09 · Insecure Skill Coding Practices
- Location
src/subtitle_client.py:99- Finding
Untrusted Subtitle Filename Allows Arbitrary File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
src/subtitle_client.py, lines 99–103
Vulnerability Type: Path traversal and unrestricted file write
Risk Level: MediumVulnerable Code:
python # 保存文件 if save_path is None: save_path = subtitle.get('file_name', 'subtitle.srt') with open(save_path, 'wb') as f: f.write(r2.content)Technical Analysis
When the caller does not provide
save_path, the application takesfile_namedirectly from thesubtitledictionary and uses it as the destination passed toopen().The filename is not reduced to a basename, normalized against an approved download directory, or checked for absolute paths and parent-directory components. Consequently, values such as
../../configuration.jsonor/home/user/.config/application.confcan cause downloaded content to be written outside the intended subtitle workspace.The
subtitledictionary is normally derived from an OpenSubtitles API response, butdownload()is a public method that also accepts caller-constructed dictionaries. Exploitation therefore does not strictly require control of the remote API: any party able to influence the dictionary supplied to this method and provide a validfile_idcan control the default output path.The use of write-binary mode (
'wb') truncates an existing destination file before writing the downloaded response, making this an overwrite primitive rather than merely an unauthorized file creation issue.Attack Path
-
The attacker obtains or identifies a valid OpenSubtitles
file_id. -
The attacker causes
download()to receive a dictionary such as:python { "file_id": VALID_FILE_ID, "file_name": "../../target-file" } -
The caller invokes
download()without an explicit trustedsave_path. -
The client requests a download link and retrieves the remote subtitle content.
-
The relative path escapes the c ...[truncated 1058 chars]
-
- Remediation
View remediation
Remediation Suggestions
Store downloads exclusively under a dedicated, trusted directory and never treat a remote filename as a path.
- Reduce the supplied filename to its basename using
Path(filename).name. - Reject empty names, absolute paths, path separators, and parent-directory components.
- Restrict accepted extensions to supported subtitle formats such as
.srtand.ass. - Resolve the final path and verify that it remains below the approved download directory.
- Avoid silently overwriting existing files; use exclusive creation or generate a unique filename.
- Treat an explicitly supplied
save_pathas untrusted unless it is similarly constrained by the surrounding application. - Validate the download response with
raise_for_status()and apply a maximum response-size limit before writing.
Example hardened approach:
python from pathlib import Path import uuid download_dir = Path("workspace/subtitles").resolve() download_dir.mkdir(parents=True, exist_ok=True) raw_name = subtitle.get("file_name") or f"{uuid.uuid4()}.srt" safe_name = Path(raw_name).name if Path(safe_name).suffix.lower() not in {".srt", ".ass"}: raise ValueError("Unsupported subtitle extension") destination = (download_dir / safe_name).resolve() if download_dir not in destination.parents: raise ValueError("Invalid subtitle path") r2.raise_for_status() with destination.open("xb") as output: output.write(r2.content)- Reduce the supplied filename to its basename using
