T09 · Insecure Skill Coding Practices
Warning
- Location
- reporters/json_export.py:75
- Finding
- Path Traversal and Arbitrary File Overwrite in Report Writers## Vulnerability Details **File Locations**: - `reporters/json_export.py:75-90` - `reporters/json_export.py:92-110` - `reporters/markdown_report.py:122-137` - `reporters/markdown_report.py:139-157` **Vulnerability Type**: Unrestricted filename handling leading to path traversal and arbitrary file overwrite **Risk Level**: Medium ### Vulnerable Code ```python # reporters/json_export.py:75-90 def save_meter(self, meter: ResponseSpeedMeter, filename: str = None) -> Path: """保存測量結果到文件""" if not self.output_dir: raise ValueError("未設置輸出目錄") self.output_dir.mkdir(parents=True, exist_ok=True) filename = filename or f"response_test_{meter.test_id}.json" filepath = self.output_dir / filename content = self.export_meter(meter) with open(filepath, 'w', encoding='utf-8') as f: f.write(content) return filepath ``` ```python # reporters/json_export.py:92-110 def save_benchmark(self, benchmark: ResponseSpeedBenchmark, filename: str = None) -> Path: """保存基準測試結果到文件""" if not self.output_dir: raise ValueError("未設置輸出目錄") self.output_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = filename or f"benchmark_{timestamp}.json" filepath = self.output_dir / filename content = self.export_benchmark(benchmark) with open(filepath, 'w', encoding='utf-8') as f: f.write(content) return filepath ``` ```python # reporters/markdown_report.py:122-137 def save_meter(self, meter: ResponseSpeedMeter, filename: str = None) -> Path: """保存測量結果到文件""" if not self.output_dir: raise ValueError("未設置輸出目錄") self.output_dir.mkdir(parents=True, exist_ok=True) filename = filename or f"response_test_{meter.test_id}.md" filepath = self.output_dir / filename content = self.export_detailed_report(meter) with open(fil ...[truncated 3496 chars]
- Remediation
- ## Remediation Suggestions 1. Resolve both the output directory and destination before writing, then enforce destination containment: ```python base = self.output_dir.resolve() base.mkdir(parents=True, exist_ok=True) if Path(filename).is_absolute(): raise ValueError("Absolute report paths are not permitted") target = (base / filename).resolve() try: target.relative_to(base) except ValueError: raise ValueError("Report path escapes the output directory") ``` 2. Restrict caller-provided values to a basename when subdirectories are unnecessary: ```python name = Path(filename) if name.name != filename or ".." in name.parts: raise ValueError("Invalid report filename") ``` 3. Enforce the expected extension, such as `.json` for `JSONReporter` and `.md` for `MarkdownReporter`. 4. Avoid silent replacement of existing files. Use exclusive creation mode unless overwriting is explicitly requested: ```python with open(target, "x", encoding="utf-8") as report_file: report_file.write(content) ``` 5. If intentional overwriting is required, expose a separate `overwrite=False` option and reject existing destinations by default. 6. Where untrusted local users can modify the output directory, mitigate symlink attacks by using a trusted directory with restrictive permissions and platform-appropriate no-follow file-opening controls. 7. Add regression tests covering absolute paths, `../` traversal, nested traversal, unexpected extensions, existing destinations, and symlink destinations.
