T09 · Insecure Skill Coding Practices
- Location
src/config_manager.py:32- Finding
Path Traversal Through Unvalidated Month Values
- Content
View full analysis
Vulnerability Details
File Location:
src/config_manager.py:32-53; equivalent path construction also occurs insrc/expense_manager.py:27-35
Vulnerability Type: Path traversal and arbitrary JSON file access
Risk Level: HighVulnerable Code
python def get_budget(month=None): """ Read monthly budget data. month: YYYY-MM format; the current month is used when omitted. """ if month is None: import datetime month = datetime.datetime.now().strftime("%Y-%m") m = month.replace("-", "") path = DATA_DIR_DATA / f"budget_{m}.json" data = _read_json(path) return data if data else None def set_budget(month, total, pools=None, daily_budget=100, weekly_budget=750, note=""): """ Set a monthly budget. """ m = month.replace("-", "") path = DATA_DIR_DATA / f"budget_{m}.json" data = { "month": month, "total": total, "pools": pools or {}, "daily_budget": daily_budget, "weekly_budget": weekly_budget, "note": note } _write_json(path, data) return dataThe corresponding expense path construction is:
python def _get_file_path(month=None): """Get the expense file path.""" if month is None: month = datetime.now().strftime("%Y-%m") m = month.replace("-", "") return DATA_DIR_DATA / f"expense_{m}.json"Technical Analysis
The
monthparameter is treated as though it always follows the documentedYYYY-MMformat, but no validation enforces that requirement. Callingreplace("-", "")only removes hyphens; it does not remove path separators or neutralize..traversal components.The resulting attacker-controlled string is interpolated into a filename and joined to
DATA_DIR_DATA. The path is neither resolved nor checked for containment under the intended data directory. For writes,_write_json()...[truncated 1458 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate every month before path construction with a strict full-string rule such as
^\d{4}-(0[1-9]|1[0-2])$. -
Parse the value using
datetime.strptime(month, "%Y-%m")and regenerate the canonical filename from the parsed date. -
Resolve the final path and verify containment before every read or write:
python base = DATA_DIR_DATA.resolve() candidate = (base / f"budget_{canonical_month}.json").resolve() if candidate.parent != base: raise ValueError("Invalid budget path") -
Apply the same centralized validation to budget and expense paths.
-
Reject path separators, null bytes, traversal components, and noncanonical date values.
-
Add tests covering
../, nested separators, absolute-path syntax, malformed months, and valid boundary months.
-
