T09 · Insecure Skill Coding Practices
- Location
script/extract_frames.py:13- Finding
Unrestricted Output Directory Permits Predictable File Overwrites
- Content
View full analysis
Vulnerability Details
File Location:
script/extract_frames.py, lines 13 and 29–30, with user-controlled input assigned at line 46
Vulnerability Type: Unrestricted filesystem output path and unsafe file overwrite
Risk Level: MediumVulnerable Code
python os.makedirs(output_dir, exist_ok=True)python output_path = os.path.join(output_dir, f"frame_{saved_count:04d}_{timestamp:.2f}s.jpg") cv2.imwrite(output_path, frame)python output_dir = sys.argv[2] if len(sys.argv) > 2 else "output/frames"Technical Analysis
The script accepts
output_dirdirectly from a command-line argument and uses it without path normalization, workspace containment validation, symlink checks, or an allowlist of approved output directories. Although the documented destination isoutput/frames, the implementation allows any location writable by the executing process.Frame names are predictable, beginning with
frame_0000_0.00s.jpg. OpenCV'scv2.imwriteoverwrites an existing file at the selected path without requiring confirmation. Its return value is also ignored, preventing the script from detecting failed or partial writes.This does not grant permissions beyond those already held by the process, and it does not provide control over arbitrary filenames. However, it permits creation of directories and replacement of matching, predictably named JPEG files outside the intended project output area.
Attack Path
- An attacker or untrusted caller supplies a crafted video and sets the second command-line argument to a writable directory outside the intended output root.
- The script passes that argument directly to
os.makedirs. - If the directory does not exist, the script creates it using the privileges of the running process.
- The script constructs predictable names such as
frame_0000_0.00s.jpg. - If files with those names already exist in the selected directory, `cv2.imwri ...[truncated 823 chars]
- Remediation
View remediation
Remediation Suggestions
- Define a trusted output root, such as a project-owned
outputdirectory. - Resolve both the trusted root and requested destination with
pathlib.Path.resolve(). - Reject the destination unless it is equal to or contained beneath the trusted root.
- Reject symbolic-link output directories and revalidate containment immediately before each write where concurrent path replacement is a concern.
- Refuse to overwrite existing files by default, or generate a unique per-run directory.
- Check the Boolean result returned by
cv2.imwriteand fail safely when writing does not succeed. - Apply limits to the number and total size of generated frames.
Example containment check:
python from pathlib import Path trusted_root = (Path.cwd() / "output").resolve() requested_dir = Path(output_dir).resolve() if requested_dir != trusted_root and trusted_root not in requested_dir.parents: raise ValueError("Output directory must be inside the approved output root") requested_dir.mkdir(parents=True, exist_ok=True)Before writing, check for an existing destination and handle write failures:
python if output_path.exists(): raise FileExistsError(f"Refusing to overwrite existing file: {output_path}") if not cv2.imwrite(str(output_path), frame): raise OSError(f"Failed to write frame: {output_path}")- Define a trusted output root, such as a project-owned
