T09 · Insecure Skill Coding Practices
- Location
scripts/init_checklist.py:129- Finding
Unrestricted Path Traversal Allows Arbitrary checklist.json Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/init_checklist.py, lines 129–140
Vulnerability Type: Path traversal and unrestricted file overwrite
Risk Level: MediumVulnerable Code
python article = sys.argv[1] version = sys.argv[2] if len(sys.argv) > 2 else "v1" reviewer = sys.argv[3] if len(sys.argv) > 3 else "笔探" review_dir = os.path.join("docs", "reviews", article) os.makedirs(review_dir, exist_ok=True) checklist = make_checklist(article, version, reviewer) output_path = os.path.join(review_dir, "checklist.json") with open(output_path, "w", encoding="utf-8") as f: json.dump(checklist, f, ensure_ascii=False, indent=2)Technical Analysis
The command-line
articleargument is incorporated directly into a filesystem path without validation or containment enforcement. Python'sos.path.join()does not prevent traversal:- An argument containing
../can escape the intendeddocs/reviewsdirectory. - An absolute argument causes preceding path components to be discarded.
os.makedirs()creates the attacker-selected directory tree.- Opening the resulting
checklist.jsonwith mode"w"creates the file or truncates an existing file.
The output filename is fixed as
checklist.json, so this is not a fully arbitrary-filename write. It nevertheless permits creation or replacement of any writable file with that name at a caller-selected directory. Existing symlinks may further affect where the write resolves because the script performs no symlink checks.Attack Path
Prerequisite: The attacker can invoke the script directly or control the article-name argument passed by another workflow. The process must have write permission at the target location.
Example traversal:
bash cd /path/to/project python3 scripts/init_checklist.py ../../attacker-controlledThe path is constructed as:
text docs/reviews/../../attacker-controlled/checklist.jsonThis resolves outside the intended
docs/reviewstree. ...[truncated 1095 chars]- An argument containing
- Remediation
View remediation
Remediation Suggestions
- Restrict article names to a safe slug format:
python import re if not re.fullmatch(r"[A-Za-z0-9_-]+", article): raise ValueError( "Article name may contain only letters, digits, underscores, and hyphens" )- Resolve and enforce destination containment as a defense-in-depth measure:
python from pathlib import Path root = (Path.cwd() / "docs" / "reviews").resolve() review_dir = (root / article).resolve() if review_dir.parent != root: raise ValueError("Article path escapes the review directory") review_dir.mkdir(parents=True, exist_ok=True) output_path = review_dir / "checklist.json"- Refuse silent replacement unless overwrite behavior is explicitly requested:
python with output_path.open("x", encoding="utf-8") as f: json.dump(checklist, f, ensure_ascii=False, indent=2)-
If replacement is required, use a dedicated
--forceoption and perform an atomic write through a securely created temporary file in the validated destination directory. -
Reject symlinked destination directories and output files where the deployment threat model includes untrusted local users. Revalidate containment immediately before the final write to reduce time-of-check/time-of-use risk.
-
Add tests covering
../, absolute paths, nested separators, symlink destinations, existing output files, and valid slug names.
