T09 · Insecure Skill Coding Practices
- Location
reporters/json_export.py:75- Finding
Path Traversal and Arbitrary File Overwrite in Report Writers
- Content
View full analysis
Vulnerability Details
File Locations:
reporters/json_export.py:75-90reporters/json_export.py:92-110reporters/markdown_report.py:122-137reporters/markdown_report.py:139-157
Vulnerability Type: Unrestricted filename handling leading to path traversal and arbitrary file overwrite
Risk Level: MediumVulnerable 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 filepathpython # 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 filepathpython # 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
View remediation
Remediation Suggestions
- 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")- 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")-
Enforce the expected extension, such as
.jsonforJSONReporterand.mdforMarkdownReporter. -
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)-
If intentional overwriting is required, expose a separate
overwrite=Falseoption and reject existing destinations by default. -
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.
-
Add regression tests covering absolute paths,
../traversal, nested traversal, unexpected extensions, existing destinations, and symlink destinations.
