T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:69- Finding
Unrestricted User-Controlled Output Path Allows File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py, lines 69–72
Vulnerability Type: Unrestricted file write and overwrite
Risk Level: MediumVulnerable Code
python if args.output: with open(args.output, 'w') as f: f.write(text) print(f"\nSaved to: {args.output}")Technical Analysis
The user-controlled
--outputargument is passed directly toopen()in write mode without canonicalization, directory-boundary validation, symlink checks, or overwrite protection.An attacker who can control the script arguments can provide:
- An absolute path outside the intended workspace.
- A relative path containing
../traversal sequences. - A path that resolves through a symbolic link.
- The path of an existing user-writable file.
Opening the destination with mode
'w'creates the file if it does not exist and silently truncates it if it does. This behavior contradicts the path-validation and workspace-restriction expectations documented inSKILL.md.The data written is generated from fixed limitation templates rather than being arbitrary attacker-controlled content. Consequently, the primary risk is file corruption or destructive overwrite, not arbitrary code insertion.
Attack Path
-
The attacker gains control over, or influences, the command-line arguments used to invoke the Skill.
-
The attacker supplies an output destination outside the intended workspace, for example:
bash python scripts/main.py \ --issues sample_size \ --output ../../user-writable-configuration -
The script accepts the path without validation.
-
Python resolves the path and opens the target in
'w'mode. -
If the process account can write to the target, the existing file is truncated and replaced with the generated study-limitations text.
A similar attack can target a symbolic link whose resolved destination is outside the allowed output directory.
Impact Assessment
The vulnerability does not g ...[truncated 809 chars]
- Remediation
View remediation
Remediation Suggestions
- Define a dedicated, trusted output directory rather than accepting unrestricted destinations.
- Resolve both the trusted directory and requested destination with
pathlib.Path.resolve(). - Verify that the resolved destination is contained within the trusted directory before writing.
- Reject absolute paths and traversal components if callers only need simple output filenames.
- Reject symbolic-link destinations, and account for race conditions when checking and opening files.
- Avoid silent replacement by using exclusive creation mode (
'x') when overwriting is unnecessary. If replacement is required, require explicit authorization. - Open files with an explicit encoding and handle filesystem errors without exposing unnecessary environment details.
- Where supported, use descriptor-based, no-follow file-opening controls to reduce symlink race risks.
Example boundary validation:
python from pathlib import Path output_root = Path.cwd().joinpath("output").resolve() output_root.mkdir(parents=True, exist_ok=True) requested = Path(args.output) if requested.is_absolute() or ".." in requested.parts: raise ValueError("Output must be a relative path within the output directory") destination = output_root.joinpath(requested).resolve() if destination != output_root and output_root not in destination.parents: raise ValueError("Output path escapes the permitted output directory") if destination.exists() or destination.is_symlink(): raise FileExistsError("Refusing to overwrite an existing file") with destination.open("x", encoding="utf-8") as f: f.write(text)For higher-assurance environments, enforce the output boundary at the sandbox or operating-system level in addition to application-level validation.
