T09 · Insecure Skill Coding Practices
- Location
scripts/log_diet.py:89- Finding
Unvalidated Date Input Permits Path Traversal and Symlink-Following File Overwrites
- Content
View full analysis
Vulnerability Details
File Location:
scripts/log_diet.py, lines 89–113
Vulnerability Type: Unvalidated filesystem path construction and unsafe file overwrite
Risk Level: Mediumpython parser.add_argument("--date", default=datetime.now().strftime("%Y-%m-%d"), help="日期 (YYYY-MM-DD)") parser.add_argument("--meal", required=True, choices=["早饭", "中饭", "晚饭", "加餐"], help="餐段") parser.add_argument("--items", required=True, help="食物列表,逗号分隔") args = parser.parse_args() # 确保目录存在 os.makedirs(DAILY_DIR, exist_ok=True) # 文件路径 filename = f"饮食记录-{args.date}.md" filepath = os.path.join(DAILY_DIR, filename) # 读取或创建文件 content = read_or_create_file(filepath, args.date) # 解析食物 items = parse_items(args.items) # 更新内容 new_content = update_meal(content, args.meal, items) # 保存文件 with open(filepath, "w", encoding="utf-8") as f: f.write(new_content)Technical Analysis
The documentation states that
--datemust use theYYYY-MM-DDformat, but the program does not enforce that constraint. The untrusted value is inserted directly into a filename and passed toos.path.join().os.path.join()does not prevent traversal components embedded after a path separator. A crafted date containing separators and..components can therefore resolve outsideDAILY_DIRwhen the required intermediate directory exists. The destination is subsequently opened in write mode, which truncates an existing target before writing the generated Markdown content.The implementation also uses ordinary
open()operations that follow symbolic links. If the predictable daily-record path is a symbolic link, both the read and write operations act on the link target. There is no canonical-path containment check, symlink rejection, exclusive creation, or atomic replacement.Attack Path
A path-traversal exploitati ...[truncated 1597 chars]
- Remediation
View remediation
Remediation Suggestions
-
Validate and canonicalize the date before using it:
python parsed_date = datetime.strptime(args.date, "%Y-%m-%d") date_str = parsed_date.strftime("%Y-%m-%d")Reject the request if parsing fails.
-
Construct the filename only from the canonical value, ensuring that user input cannot introduce separators or traversal components.
-
Resolve and verify the destination path:
python base = os.path.realpath(DAILY_DIR) filepath = os.path.realpath( os.path.join(base, f"饮食记录-{date_str}.md") ) if os.path.dirname(filepath) != base: raise ValueError("Destination is outside the daily-record directory") -
Reject symbolic-link destinations using
os.path.islink()and, where supported, open files with protections such asos.O_NOFOLLOW. -
Write through a securely created temporary file in the same directory and atomically replace the destination after validation. This reduces partial-write and corruption risks.
-
Apply restrictive file permissions and ensure that untrusted local users cannot create files, directories, or symbolic links inside
DAILY_DIR.
-
