T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:67- Finding
Unrestricted Output Path Allows Arbitrary File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py, lines 67 and 88-89
Vulnerability Type: Unrestricted user-controlled file write
Risk Level: MediumVulnerable Code
python parser.add_argument("--output", "-o", default="value_proposition.txt", help="Output file")python with open(args.output, 'w') as f: f.write(text)Technical Analysis
The
--outputargument accepts an arbitrary path and passes it directly toopen()in truncating write mode. The implementation does not normalize or validate the path, confine it to an approved workspace, reject path traversal, check for symbolic links, or require confirmation before overwriting an existing file.Consequently, absolute paths and traversal paths such as
../target-filecan select any destination writable by the process. A symbolic link at the selected path could also redirect the write to another writable destination. Opening the resolved file with mode'w'truncates it before writing the generated value-proposition text.This behavior contradicts the output-directory restriction documented in
SKILL.md, but that restriction is not enforced by the executable implementation.Attack Path
- An attacker or untrusted caller controls the
--outputcommand-line argument. - The caller supplies an absolute path, a
../traversal path, or a path resolving through a symbolic link. - The script passes the supplied value directly to
open(args.output, 'w'). - The operating system resolves the destination using the script process's permissions.
- If the destination exists and is writable, it is truncated and replaced with generated text. If it does not exist but its parent directory is writable, a new file is created.
Example invocation demonstrating the vulnerable data flow:
bash python scripts/main.py \ --name ExampleDrug \ --indication ExampleIndication \ --output ../target-file...[truncated 692 chars]
- An attacker or untrusted caller controls the
- Remediation
View remediation
Remediation Suggestions
- Define an approved output root and resolve all requested output paths relative to it.
- Use
pathlib.Path.resolve()and verify that the resolved destination remains beneath the approved root. - Reject absolute paths and paths containing traversal outside the output root.
- Reject symbolic-link destinations and validate parent path components to mitigate symlink redirection.
- Avoid silent truncation. Use exclusive creation mode (
'x') by default or require explicit overwrite authorization. - Where replacement is necessary, write to a securely created temporary file inside the approved directory and atomically replace the intended destination after validation.
- Handle validation and file-system errors with sanitized messages and a nonzero exit status.
- Add tests covering absolute paths,
../traversal, existing-file overwrite, symbolic links, missing parent directories, and valid workspace-local destinations.
A confinement check should follow this pattern:
python from pathlib import Path output_root = (Path.cwd() / "outputs").resolve() output_root.mkdir(parents=True, exist_ok=True) requested = Path(args.output) if requested.is_absolute(): raise ValueError("Absolute output paths are not allowed") destination = (output_root / requested).resolve() if output_root != destination and output_root not in destination.parents: raise ValueError("Output path escapes the approved directory") if destination.exists(): raise FileExistsError("Refusing to overwrite an existing file") with destination.open("x", encoding="utf-8") as file: file.write(text)
