T09 · Insecure Skill Coding Practices
- Location
scripts/extract_swim_data.py:30- Finding
Path Traversal in Date-Based Record Storage and Lookup
- Content
View full analysis
dict: """按日期归档保存,同一天多次训练追加到数组。返回结果字典。""" date_str = data["date"] # YYYY-MM-DD year, month, _ = date_str.split("-") dir_path = DATA_DIR / year / month dir_path.mkdir(parents=True, exist_ok=True) file_path = dir_path / f"{date_str}.json" # 同一天的数据直接覆盖(用户可能重发截图以更正数据) file_path.write_text(json.dumps([data], ensure_ascii=False, indent=2), encoding="utf-8") return { "status": "saved", "file": str(file_path), "date": date_str, "total_distance": data["total_distance"], } def check_date(date_str: str) -> dict: """检查某日期是否已有训练记录。""" year, month, _ = date_str.split("-") file_path = DATA_DIR / year / month / f"{date_str}.json" if file_path.exists(): existing = json.loads(file_path.read_text(encoding="utf-8")) if not isinstance(existing, list): existing = [existing] return { "exists": True, "date": date_str, "sessions": len(existing), "data": existing, } return {"exists": False, "date": date_str} ``` ### Technical Analysis The `date` value is expected to use the `YYYY-MM-DD` format, but the implementation only splits it on hyphens. It does not validate the resulting components, reject path separators or traversal sequences, parse the value as a real calendar date, or verify that the resolved destination remains beneath `DATA_DIR`. Both `year` and `month` are incorporated directly into filesystem paths. The complete untrusted `date_str` is also incorporated into the filename. A value containing directory separators and `..` components can consequently alter the resolved path. The `save()` operation creates attacker-influenced directories ...[truncated 1974 chars]- Remediation
View remediation
str: if not isinstance(value, str): raise ValueError("date must be a string") parsed = datetime.strptime(value, "%Y-%m-%d") canonical = parsed.strftime("%Y-%m-%d") if value != canonical: raise ValueError("date must use canonical YYYY-MM-DD format") return canonical ``` 2. Construct directories only from the parsed date rather than raw substrings: ```python date_str = canonical_date(data["date"]) parsed = datetime.strptime(date_str, "%Y-%m-%d") file_path = DATA_DIR / parsed.strftime("%Y") / parsed.strftime("%m") / f"{date_str}.json" ``` 3. Enforce containment before every read or write: ```python data_root = DATA_DIR.resolve() resolved_path = file_path.resolve() if not resolved_path.is_relative_to(data_root): raise ValueError("resolved path escapes the data directory") ``` 4. Apply the same canonical validation and containment checks to both `save()` and `check_date()`. 5. Reject path separators, NUL characters, traversal components, and non-string date values before filesystem operations. 6. Avoid returning absolute local paths unless callers require them. 7. Add regression tests covering `../`, absolute paths, embedded separators, invalid dates, extra hyphens, Unicode separator-like characters, and symlinks beneath the data directory. ]]>
