T09 · Insecure Skill Coding Practices
- Location
scripts/scrape_base.py:275- Finding
Path Traversal Through Unsanitized Module Identifier
- Content
View full analysis
Vulnerability Details
File Location:
scripts/scrape_base.py, lines 275–278
Vulnerability Type: Output path traversal
Risk Level: Mediumpython safe_name = mod["name"].replace("/", "-") file_path = self.output_dir / f"{mid}_{safe_name}.json" with open(file_path, "w", encoding="utf-8") as f: json.dump({Technical Analysis
The module identifier
midis incorporated directly into a filesystem path without validation or sanitization. Although forward slashes inmod["name"]are replaced, the same protection is not applied tomid, and the resulting path is not resolved and checked against the configured output directory.In subclasses that derive module identifiers from scraped website content, a malicious or compromised documentation site could supply an identifier containing traversal components such as
../. Python's path composition preserves these components, andopen(..., "w")follows them when creating the output file.Attack Path
- An attacker controls or compromises an API documentation page processed by the scraper.
- The platform-specific
expand_menu()implementation copies a remote module identifier such as../../targetinto the returnedmodulesdictionary. scrape()passes that dictionary tosave_results().save_results()combines the untrusted identifier withself.output_dir.- The operating system resolves the traversal components when
open()is called. - The generated JSON is written outside the intended output directory.
Exploitation depends on a subclass propagating attacker-controlled page data into
midwithout prior validation.Impact Assessment
An attacker could create or overwrite files reachable by the scraper process outside the configured output directory. The operation is limited to the filesystem permissions of the process account, and the written content is JSON generated from the scraped module ...[truncated 197 chars]
- Remediation
View remediation
Remediation Suggestions
- Treat both
midandmod["name"]as untrusted input. - Convert identifiers to filenames using a strict allowlist, such as ASCII letters, digits, underscores, and hyphens.
- Reject empty names and special path components such as
.and... - Resolve the candidate output path and verify that it remains beneath the resolved output directory before opening it.
- Prefer locally generated stable identifiers instead of remote identifiers for filenames.
- Use exclusive file creation where overwriting existing files is unnecessary.
Example containment pattern:
python safe_mid = re.sub(r"[^A-Za-z0-9_-]", "_", str(mid)) safe_name = re.sub(r"[^A-Za-z0-9_-]", "_", str(mod["name"])) output_root = self.output_dir.resolve() file_path = (output_root / f"{safe_mid}_{safe_name}.json").resolve() if output_root not in file_path.parents: raise ValueError("Output path escapes the configured directory") with open(file_path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2)- Treat both
