T09 · Insecure Skill Coding Practices
- Location
scripts/reading_cli.py:63- Finding
Path Traversal and Arbitrary File Overwrite Through Unsanitized Book Titles
- Content
View full analysis
Vulnerability Details
File Location:
scripts/reading_cli.py, lines 63–85; related reuse at lines 134–148 and 208–219
Vulnerability Type: Path traversal and arbitrary file write
Risk Level: HighVulnerable Code
python month = datetime.now().strftime("%Y-%m") file_name = f"{title}-{month}.md" file_path = BOOKS_DIR / file_name # Create note file content = f"""# 《{title}》 ## Metadata - Author: {author or "(待填写)"} - Started: {today} - Finished: - Progress: 0% - Rating: ## Key Outputs - One-sentence summary: - Mindset shift: - Action plan: ## Quotes and Thoughts ## Review Log """ file_path.write_text(content, encoding="utf-8")The generated path is subsequently stored and trusted during quote and completion operations:
python file_path = Path(book["filePath"]) if file_path.exists(): content = file_path.read_text(encoding="utf-8") # ... file_path.write_text(content, encoding="utf-8")Technical Analysis
The
titlevalue originates from a command-line argument and is embedded directly intofile_namewithout validation or canonicalization. Path components such as../, platform-specific path separators, and absolute path prefixes are therefore interpreted bypathlibas filesystem navigation rather than as literal title characters.Joining
BOOKS_DIRwith such an attacker-controlled path does not ensure that the result remains inside the intended books directory. A crafted title can causewrite_text()to create or overwrite a file elsewhere in the filesystem, provided the process user can write to the target location. The automatically appended-YYYY-MM.mdsuffix limits the exact target filename but does not prevent escape fromBOOKS_DIR.The resulting path is persisted in
library.json. Later calls toadd_quote()andsave_finish_answers()reconstruct aPathfrom this stored value and modify the referenced file withou ...[truncated 1775 chars]- Remediation
View remediation
Remediation Suggestions
-
Use trusted identifiers for filenames. Generate note filenames from the existing UUID-based
book_idrather than from the user-supplied title:python file_name = f"{book_id}-{month}.md"Preserve the original title only inside the note content and library metadata.
-
Apply strict title or slug validation. If human-readable filenames are required, convert titles to a restricted slug containing only an explicitly permitted character set. Reject path separators, absolute paths,
.and..components, null bytes, and control characters. -
Enforce containment after canonicalization. Resolve both the base directory and candidate path, then verify that the candidate remains beneath
BOOKS_DIRbefore any read or write:python base = BOOKS_DIR.resolve() candidate = (base / file_name).resolve() if candidate.parent != base: raise ValueError("Invalid book title or note path")If nested directories are intentionally supported, use
candidate.is_relative_to(base)on supported Python versions instead of requiring the immediate parent to equalbase. -
Revalidate persisted paths. Before using
book["filePath"]inadd_quote(),save_finish_answers(), or report generation, canonicalize it and enforce the same containment rule. Prefer storing a trusted book ID or relative filename rather than an absolute path. -
Prevent unintended overwrites. Create new note files using exclusive creation semantics where appropriate, and define explicit collision handling rather than silently replacing an existing file.
-
Add regression tests. Test absolute paths, repeated
../traversal, Windows separators and drive prefixes, symbolic-link escape scenarios, control characters, and ordinary Unicode book titles.
-
