T05 · Unauthorized Access and Privilege Escalation
- Location
mcp/mcp_server.py:140- Finding
MCP Run Identifier Path Traversal Allows Unauthorized File Disclosure
- Content
View full analysis
Dict[str, Any]: """Check audit run status.""" run_id = args["run_id"] run_dir = self.base_dir / run_id try: manifest = load_manifest(run_dir) except FileNotFoundError: return {"content": [{"type": "text", "text": f"Run not found: {run_id}"}], "isError": True} ``` ```python def _tool_audit_report(self, args: Dict[str, Any]) -> Dict[str, Any]: """Get audit report.""" run_id = args["run_id"] fmt = args.get("format", "markdown") run_dir = self.base_dir / run_id ext = "md" if fmt == "markdown" else "json" report_path = run_dir / "output" / f"report.{ext}" if not report_path.exists(): return {"content": [{"type": "text", "text": f"Report not found: {report_path}"}], "isError": True} content = report_path.read_text(encoding="utf-8") ``` ```python def _tool_audit_metrics(self, args: Dict[str, Any]) -> Dict[str, Any]: """Get computed metrics, optionally filtered by tier.""" run_id = args["run_id"] tier_filter = args.get("tier", "all") run_dir = self.base_dir / run_id metrics_path = run_dir / "intermediate" / "metrics.json" if not metrics_path.exists(): return {"content": [{"type": "text", "text": "Metrics not found. Run calculate_metrics.py first."}], "isError": True} metrics = read_json(metrics_path) ``` ### Technical Analysis The MCP handlers accept `run_id` directly from tool-call arguments and combine it with `self.base_dir` without validating the identifier or applying the project's existing `resolve_under()` containment function. A relative value containing `../` components can escape the configured audit directory ...[truncated 1979 chars]- Remediation
View remediation
Path: if not isinstance(run_id, str) or not RUN_ID_PATTERN.fullmatch(run_id): raise ValueError("Invalid run identifier") return resolve_under(base_dir, run_id) ``` 3. Use the same centralized resolver in `_tool_audit_status()`, `_tool_audit_report()`, and `_tool_audit_metrics()`. 4. After resolving the path, verify that it represents a valid audit run and that the manifest's stored `run_id` equals the requested identifier. 5. Reject symlink-based escapes by resolving the final target and confirming that it remains beneath the resolved base directory before every read. 6. Add regression tests covering: - `../` traversal. - Absolute Unix and Windows paths. - Encoded or mixed-separator traversal. - Symlink escapes. - Valid generated run identifiers. ]]>
