T09 · Insecure Skill Coding Practices
- Location
scripts/serve_dashboard.py:32- Finding
Unauthenticated Workspace File Disclosure Through the Dashboard Server
- Content
View full analysis
Vulnerability Details
File Location:
scripts/serve_dashboard.py:32-33, 83-84, 94
Vulnerability Type: Unauthenticated local file disclosure
Risk Level: MediumComplete Code Snippet
python class Handler(http.server.SimpleHTTPRequestHandler): def __init__(self, *a, **kw): super().__init__(*a, directory=ROOT, **kw)python if path == '/': self.path = '/dashboard.html' return super().do_GET()python with Server(('127.0.0.1', port), Handler) as httpd:The Skill directs the agent to start this server when the user asks to open the dashboard (
SKILL.md:21).Technical Analysis
Handlerinherits fromSimpleHTTPRequestHandlerand configures the entire selected workspace as its document root. Requests not handled by/api/treeor/api/fileare delegated to the inherited static-file handler.Consequently, the safeguards in
/api/file—the workspace path-prefix check, text-extension allowlist, and 400 KB read limit—do not protect the fallback route. Any file accepted by the static handler can be requested directly, including dotfiles such as/.env. This is particularly relevant becauseSKILL.md:53tells users to store secrets in.env.Although the service binds only to
127.0.0.1, it does not authenticate clients or distinguish the workspace owner from other local users or sandboxed processes that can connect to the loopback port. The dashboard process accesses files with the privileges of the user who started it.Attack Path
- The user asks to open or inspect the marketing dashboard.
- The agent follows
SKILL.md:21and startsserve_dashboard.pyfor the selected workspace. - An untrusted local process or another local user able to connect to the loopback port discovers or predicts the default port,
8799. - The attacker sends a direct request such as:
text GET http://127.0.0.1:8799/.en
...[truncated 810 chars]
- Remediation
View remediation
Remediation Suggestions
- Do not use the workspace root as an unrestricted
SimpleHTTPRequestHandlerdocument root. - Implement explicit routes that serve only
dashboard.htmland a narrowly defined set of required assets. - Deny dotfiles and sensitive names, including
.env, credential files, private keys, cache data, and repository metadata. - Canonicalize each requested path with
os.path.realpath()orPath.resolve()and verify that the resolved path remains under the resolved workspace root before reading it. - Apply a strict extension and filename allowlist to every file-serving route, not only
/api/file. - Require an unguessable per-launch token for dashboard and API requests, or use an owner-restricted local transport where practical.
- Return
404or403for every unrecognized route instead of delegating it to the generic static-file handler. - Add regression tests confirming that requests for
/.env,/.git/config, cache files, unknown paths, and paths resolving outside the workspace are rejected.
- Do not use the workspace root as an unrestricted
