T09 · Insecure Skill Coding Practices
- Location
tools/md2docx.py:188- Finding
Predictable Temporary File Allows File Overwrite and Deletion
- Content
View full analysis
Vulnerability Details
File Location:
tools/md2docx.py, lines 188–192 and 262–264
Vulnerability Type: Predictable and non-exclusive temporary-file handling
Risk Level: MediumVulnerable Code
python date_line = f"\n\n---\n\n{datetime.now().strftime('%Y年%m月%d日')}" temp_input_file = input_file.with_suffix(input_file.suffix + '.temp.md') try: with open(temp_input_file, 'w', encoding='utf-8') as f: f.write(content + date_line) # The temporary file is subsequently supplied to Pandoc. # ... finally: # Clean up temporary file if temp_input_file.exists(): temp_input_file.unlink()Technical Analysis
The converter constructs a deterministic temporary path by appending
.temp.mdto the input filename. It then opens that path in write mode without exclusive creation or protection against symbolic links.If the generated path already contains a regular file, the converter truncates and overwrites it. On platforms where symbolic links are supported, an attacker able to modify the input directory can pre-create the predictable path as a symbolic link to another file writable by the converter process. Opening the link in write mode follows it and replaces the target's contents with the generated Markdown. The cleanup operation then removes the predictable directory entry.
The existence check before
unlink()also introduces a time-of-check/time-of-use window. An attacker with concurrent directory access may replace the path between the check and deletion, although deletion normally affects the directory entry rather than the target of a symbolic link.No shell-command injection was identified in the Pandoc invocation because
subprocess.run()receives an argument list and does not enable a shell.Attack Path
- The victim selects an input such as
/shared/report.md. - The attacker predicts that the converter will use
/shared/report.md.temp.md. - Before conversion, the attacker creates th ...[truncated 1341 chars]
- The victim selects an input such as
- Remediation
View remediation
Remediation Suggestions
- Replace the deterministic filename with a securely and atomically created temporary file using
tempfile.NamedTemporaryFile(delete=False)ortempfile.mkstemp(). - Prefer a private, controlled temporary directory rather than the input file's directory.
- Close and flush the temporary file before invoking Pandoc, particularly for cross-platform compatibility.
- Retain the exact uniquely generated path and remove only that path in the
finallyblock. - Avoid separate existence checks before cleanup. Attempt deletion directly and handle
FileNotFoundError. - If a temporary file must be created beside the input, use exclusive creation and platform-appropriate protections against symbolic-link traversal.
- Add tests covering:
- A pre-existing candidate temporary file.
- A symbolic link at the candidate path on supported platforms.
- Concurrent replacement or removal during cleanup.
- Cleanup after Pandoc failure.
A safer implementation pattern is:
python import os import tempfile temp_path = None try: fd, temp_name = tempfile.mkstemp( suffix=".md", prefix=f".{input_file.stem}-", dir=None, text=True, ) temp_path = Path(temp_name) with os.fdopen(fd, "w", encoding="utf-8") as temp_file: temp_file.write(content + date_line) # Invoke Pandoc with temp_path. finally: if temp_path is not None: try: temp_path.unlink() except FileNotFoundError: pass- Replace the deterministic filename with a securely and atomically created temporary file using
