T09 · Insecure Skill Coding Practices
- Location
scripts/multiplier.py:267- Finding
Arbitrary File Overwrite Through Unsanitized Topic
- Content
View full analysis
Vulnerability Details
File Location:
scripts/multiplier.py, lines 267–271
Vulnerability Type: Path traversal leading to arbitrary file creation or overwrite
Risk Level: MediumVulnerable Code:
python filename = f"{topic.replace(' ', '_')}_{platform}.md" filepath = os.path.join(output_dir, filename) with open(filepath, 'w', encoding='utf-8') as f: f.write(content)Technical Analysis
The user-controlled
--topicargument is incorporated directly into an output filename. Replacing spaces with underscores does not remove path separators, absolute-path prefixes, or..traversal components.The resulting path is joined with
output_dirwithout canonicalization or a containment check. Opening that path with modewcreates the file if it does not exist and truncates it if it does. Python will also follow a symbolic link at the destination.Although the selected platform adds a fixed suffix such as
_linkedin.md, an attacker can still target writable paths whose final names include that suffix.Attack Path
-
The attacker invokes the script with a crafted topic containing traversal components, for example:
bash python scripts/multiplier.py \ --input source.txt \ --platforms linkedin \ --topic "../../target" \ --output ./generated_content -
The script constructs the filename:
text ../../target_linkedin.md -
os.path.join()produces:text ./generated_content/../../target_linkedin.md -
Filesystem path resolution escapes the intended output directory.
-
The script creates or truncates
target_linkedin.mdoutside that directory and writes generated Markdown into it. -
If the calculated destination is a symbolic link, the linked target may be overwritten instead.
Exploitation requires control over the command-line topic and write permission to the resolved destination.
...[truncated 549 chars]
-
- Remediation
View remediation
Remediation Suggestions
- Convert the topic into a strict filename slug using an allowlist, such as ASCII letters, digits, underscores, and hyphens.
- Reject absolute paths, path separators,
..components, empty slugs, and reserved filenames. - Resolve both the output directory and candidate destination with
pathlib.Path.resolve(), then verify that the destination remains beneath the resolved output directory. - Reject existing symbolic-link destinations and consider using
os.open()withO_NOFOLLOWon supported platforms. - Use exclusive creation mode (
x) when overwriting existing output is not required. Otherwise, require explicit overwrite authorization. - Add tests covering
../, absolute paths, nested separators, encoded or alternate separators, and symlink destinations.
Example hardening approach:
python from pathlib import Path import re safe_topic = re.sub(r"[^A-Za-z0-9_-]+", "_", topic).strip("_") if not safe_topic: raise ValueError("Topic does not produce a valid filename") output_root = Path(output_dir).resolve() output_root.mkdir(parents=True, exist_ok=True) destination = (output_root / f"{safe_topic}_{platform}.md").resolve() if destination.parent != output_root: raise ValueError("Output path escapes the configured directory") if destination.is_symlink(): raise ValueError("Symbolic-link destinations are not permitted") with destination.open("x", encoding="utf-8") as f: f.write(content)
