T09 · Insecure Skill Coding Practices
- Location
scripts/open_dashboard.py:301- Finding
Unauthenticated dashboard endpoint exposes sensitive files throughout the project root
- Content
View full analysis
Path | None: candidate = (ROOT / relative).resolve() try: candidate.relative_to(ROOT.resolve()) except ValueError: return None return candidate if candidate.is_file() else None ``` ```python if parsed.path == "/api/file": relative = parse_qs(parsed.query).get("path", [""])[0] path = inside_root(relative) if not path: self.json({"error": "Path not allowed"}, 403); return try: content = path.read_text(encoding="utf-8") except UnicodeDecodeError: self.json({"path": relative, "binary": True, "content": "This is a binary file; view the original under sources/."}); return self.json({"path": relative, "binary": False, "content": content}); return ``` ```python server = ThreadingHTTPServer(("127.0.0.1", args.port), Handler) ``` ### Technical Analysis The dashboard binds only to the loopback interface, which prevents direct access from other hosts under normal network conditions. However, the HTTP service does not implement authentication, an unguessable per-launch access token, Origin validation, or Host-header validation. The `/api/file` endpoint accepts a caller-controlled relative path and allows access to any regular UTF-8 file whose resolved path remains under the project root. Its authorization boundary is therefore the entire repository rather than the limited set of files needed by the dashboard. This includes highly sensitive data such as: - Immutable personal-message captures under `sources/conversation/`; - Personal attachments and source material; - Derived records under `memory/`; - Backup configuration and state; - Scripts, tests, migration files, and other repo ...[truncated 2164 chars]- Remediation
View remediation
` and `[::1]:`. - Reject attacker-controlled hostnames even if they resolve to loopback. - This materially reduces DNS-rebinding exposure. 3. **Validate request origins** - Reject cross-origin requests. - Set a restrictive Content Security Policy. - Return `X-Content-Type-Options: nosniff`. - Avoid permissive CORS headers. 4. **Apply least-privilege file allowlisting** - Restrict `/api/file` to directories the dashboard genuinely needs, such as selected paths under `memory/` and `sources/`. - Deny configuration files, scripts, tests, backups, and arbitrary project files by default. - Consider exposing records through purpose-built API methods instead of a generic file-reading endpoint. 5. **Retain canonical-path checks** - Continue using `resolve()` and `relative_to()` to prevent traversal. - Explicitly reject symlinks if they are unnecessary, including symlinks that resolve elsewhere within the project but cross sensitivity boundaries. 6. **Limit service lifetime** - Shut down the server when the dashboard window closes or after an inactivity timeout. - Clearly display that a local file-reading service is active. ]]>
