T09 · Insecure Skill Coding Practices
- Location
scripts/release_check.py:503- Finding
Attacker-Controlled Output Symlink Can Overwrite Files Outside the Project
- Content
View full analysis
Vulnerability Details
File Location:
scripts/release_check.py:503-505
Vulnerability Type: Symbolic-link-following arbitrary file overwrite
Risk Level: MediumVulnerable Code
python out_path = os.path.abspath(a.out or default_out(root)) os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True) with open(out_path, "w", encoding="utf-8") as fh: fh.write(render(root, items, generated))Technical Analysis
The documented workflow permits writing the report to a relative path inside the scanned repository, such as:
bash python3 scripts/release_check.py . --out ./release-report.mdThe implementation converts that path to an absolute lexical path but does not:
- Check whether the destination is a symbolic link.
- Resolve the destination and enforce containment within an authorized output directory.
- Use no-follow file-opening semantics.
- Create a new output file atomically.
Python's
open(..., "w")follows symbolic links and truncates the resolved destination. Consequently, a repository contributor can supplyrelease-report.mdas a symbolic link to another file writable by the user running the Skill.This crosses the trust boundary between repository-controlled filesystem metadata and the user's filesystem. The user authorizes creation of a report at the selected repository path, but the repository can redirect that write to a different path.
There is no evidence that the project intentionally creates malicious links or targets user files; this is an exploitable coding flaw rather than evidence of a backdoor.
Attack Path
- An attacker controls or contributes content to a repository that the victim will scan.
- The attacker adds a symbolic link such as:
text release-report.md -> /home/victim/.config/example/config - The victim clones or otherwise obtains the repository with the symbolic link preserved.
- Following the documented usage, the victim runs:
bash python3 scripts/rel
...[truncated 947 chars]
- Remediation
View remediation
Remediation Suggestions
- Reject an existing output path when
os.lstat()shows that it is a symbolic link. - Resolve the destination with
os.path.realpath()and verify that it remains within an explicitly authorized output directory. - Open the destination with no-follow semantics, such as
os.open()withO_NOFOLLOW, where supported. - Use exclusive or atomic creation to reduce race conditions:
- Create a new temporary file in the authorized destination directory.
- Write and flush the report.
- Atomically rename it to the final destination after validating the destination.
- Refuse non-regular output files, including device nodes, FIFOs, and sockets.
- If overwriting an existing regular file is required, require an explicit overwrite option and validate the file again immediately before replacement.
- Reject an existing output path when
