T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/web_dashboard.py:356
- Finding
- Unauthenticated Arbitrary JSON File Read Through Dashboard Path Traversal## Vulnerability Details **File Location**: `scripts/web_dashboard.py`, lines 356–363 **Vulnerability Type**: Path traversal leading to unauthorized local file disclosure **Risk Level**: Medium ### Vulnerable Code ```python if parsed.path == '/detail': params = parse_qs(parsed.query) fname = params.get('file', [''])[0] if not fname: self._send_json({'error': '缺少 file 参数'}, 400) return state_path = os.path.join(self.state_dir, fname) state = get_state_detail(state_path) ``` The resulting path is passed to the following file-reading function at lines 87–94: ```python def get_state_detail(state_path): """读取单个流水线的完整状态(供详情页/甘特图使用)""" try: with open(state_path, 'r', encoding='utf-8') as f: state = json.load(f) return state except (json.JSONDecodeError, IOError): return None ``` ### Technical Analysis The `/detail` route accepts the `file` query parameter from an unauthenticated HTTP request. The value is appended to the configured state directory with `os.path.join()`, but the resolved path is never checked to ensure it remains inside that directory. Path normalization alone would not be sufficient unless the normalized path is also constrained to the intended directory. Values containing `../` components can escape `state_dir`. An absolute path can also cause `os.path.join()` to discard the state directory entirely. The selected file is opened with the dashboard process's filesystem permissions. The only content restriction is that it must parse as JSON. The dashboard is bound to `127.0.0.1`, which limits remote exposure but does not authenticate local callers; other local users, browser-driven requests, or local processes may still reach the service. ### Attack Path 1. The victim starts the dashboard against a directory containing pipeline state files. 2. An attacker capable of sending requests to the local dashboard identifies a readable JSON file outside that directory. 3 ...[truncated 1013 chars]
- Remediation
- ## Remediation Suggestions 1. Resolve the configured directory and requested path before opening the file: ```python from pathlib import Path base = Path(self.state_dir).resolve() requested = (base / fname).resolve() try: requested.relative_to(base) except ValueError: self._send_json({"error": "Invalid file path"}, 400) return ``` 2. Reject absolute paths, empty names, path separators, and traversal components. 3. If only direct child files are required, accept a basename and enforce: ```python if fname != os.path.basename(fname): self._send_json({"error": "Invalid filename"}, 400) return ``` 4. Build a server-side map of pipeline identifiers to known state files and let clients submit only an opaque identifier, not a filesystem path. 5. Require the target to be a regular `.json` file discovered by `find_pipelines()`. 6. Consider authenticating all dashboard routes, not only the approval endpoint, when pipeline state can contain sensitive information.
