T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_data.py:48- Finding
Path Traversal Enables Arbitrary JSON File Read, Modification, or Deletion
- Content
View full analysis
Path: """获取手动补充数据文件路径""" MANUAL_DIR.mkdir(parents=True, exist_ok=True) return MANUAL_DIR / f"{date}.json" ``` ```python # scripts/补数据.py:44-58 # 确定日期 if args.date: date = args.date else: date = datetime.now().strftime('%Y-%m-%d') manual_path = get_manual_path(date) # 清除手动数据 if args.clear: if manual_path.exists(): manual_path.unlink() print(f"✅ 已清除手动数据:{manual_path}") else: print(f"ℹ️ 没有手动数据可清除") return ``` ```python # scripts/补数据.py:86-107 # 加载现有数据 existing_data = load_manual_data(date) # 更新数据 if 'volume' not in existing_data: existing_data['volume'] = {} if args.today is not None: existing_data['volume']['today'] = args.today print(f"✅ 今日成交量:{args.today}万亿") if args.previous is not None: existing_data['volume']['previous'] = args.previous print(f"✅ 昨日成交量:{args.previous}万亿") # 保存数据 save_manual_data(date, existing_data) ``` ### Technical Analysis The positional `date` argument is treated as a trusted filename component without validating that it is a canonical `YYYY-MM-DD` date. It can therefore contain: - An absolute path, such as `/tmp/target` - Parent-directory components, such as `../../target` - Other path separators that escape the intended `manual/` directory With `pathlib`, joining a base path to an absolute path discards the base path. For example: ```python MANUAL_DIR / "/tmp/target.json" ``` resolves to `/tmp/target.json`, not a file beneath `MANUAL_DIR`. Relative traversal components are also retained, allowing the resulting path to escape the intended directory after filesystem resolution. The constructed path is subsequently passed ...[truncated 2444 chars]- Remediation
View remediation
str: try: parsed = datetime.strptime(value, "%Y-%m-%d") except ValueError as exc: raise ValueError("Date must use YYYY-MM-DD format") from exc canonical = parsed.strftime("%Y-%m-%d") if value != canonical: raise ValueError("Date must be a canonical YYYY-MM-DD value") return canonical ``` 2. Resolve the base directory and candidate path, then enforce containment: ```python def get_manual_path(date: str) -> Path: safe_date = validate_date(date) base = MANUAL_DIR.resolve() base.mkdir(parents=True, exist_ok=True) candidate = (base / f"{safe_date}.json").resolve() if candidate.parent != base: raise ValueError("Manual-data path escapes the allowed directory") return candidate ``` 3. Apply the same validation to every date-derived cache or report filename, even where current date parsing happens indirectly. 4. Reject all absolute paths, directory separators, parent-directory components, and noncanonical dates before filesystem access. 5. Consider protection against symbolic-link attacks when the workspace may be writable by untrusted users. Use controlled file creation, atomic replacement, restrictive permissions, and platform-appropriate no-follow semantics. 6. Add automated tests covering: - Absolute path input - `../` traversal - Nested path separators - Invalid and noncanonical dates - Leap-year and impossible calendar dates - Symlinks inside the manual-data directory - Attempts to read, overwrite, or delete files outside `MANUAL_DIR` ]]>
