Repository navigation
Description
Security audit · 2026-11-07
Scope: static review of main at 2f358bc (v0.88.1), plus read-only checks of the installed dashboard service and non-secret deployment settings. No live attack, credential rotation, or configuration change was performed. Findings below are code-level risks, not claims of observed exploitation.
P1 — Session authorization is indefinitely reusable and can outlive policy changes
The signed dashboard session stores is_admin and the login but no issue/expiry time (encoding). Every request trusts the cookie's embedded profile (current_profile); allowlists and admin membership are evaluated only during OAuth callback (callback). The session cookie has no max_age/expires (cookie). A previously issued/stolen cookie remains valid server-side after a user's access is revoked, until the signing key is rotated. Browser session-cookie lifetime is not a server-side expiry.
- Add signed
iat/exp, enforce a short server-side lifetime, and provide logout/revocation (or a session version). - Re-evaluate dashboard allowlist and admin status on requests or at a bounded interval; document how org/team removal takes effect.
- Add tests for expiry, allowlist removal, admin demotion, and key rotation.
P2 — State-changing cookie-authenticated routes have no explicit CSRF/origin check
Admin and user actions, including executor pause/resume/resize, job retry/dismiss/cancel, and MCP token creation, rely on a signed session cookie but do not validate a CSRF token or Origin/Referer. SameSite=Lax is present, but does not prevent same-site, cross-origin requests (e.g. a less-trusted sibling subdomain). This is conditional on a victim visiting such an origin; no exploit was attempted.
- Require same-origin
Originvalidation (with carefully handled absent headers) and/or a per-session CSRF token for all cookie-authenticated mutation methods. - Keep bearer-token MCP and signed webhook endpoints outside that cookie-CSRF mechanism; add cross-origin regression tests for each mutation class.
P2 — MCP token ownership is not used to scope repository knowledge
/api/mcp authenticates a bearer token but discards the returned owner record (handler). MCPServer then lists all repositories and accepts any repository in resources/read and list_knowledge (methods). Thus every valid MCP token can read all stored bridge knowledge, including other repositories. If global sharing is intentional, the current per-user token ownership is misleading and needs an explicit trust decision.
- Decide/document whether knowledge is global or repository-scoped; if scoped, bind token owner to allowed repos and enforce the check in both tool and resource paths.
- Add tests with two token owners and two repositories proving no cross-scope reads.
P3 — Webhook body limit is enforced after full body buffering
Without a trustworthy Content-Length, the ingress calls await request.body() before comparing its size to webhook_max_bytes (handler). This can allocate much more than the configured limit before rejecting an unauthenticated request. The repository's example nginx route does not set an explicit client_max_body_size for the endpoint. Actual exposure depends on the deployed proxy limits; this was not load-tested.
- Stream request chunks with an incremental hard cap, return 413 before full buffering, and set an explicit matching proxy body limit.
- Test oversized chunked/no-
Content-Lengthrequests without reading the whole stream.
Controls observed / limits
- Webhook HMAC-SHA256 uses constant-time comparison; OAuth callback checks a signed state cookie. The installed deployment has dashboard user/admin allowlists configured, a restricted (
0600) env file, and a loopback dashboard listener. These mitigate some exposure but do not remove the findings above. npm audit --omit=devreported 0 production frontend dependency advisories for the lockfile on this date. This is not a Python dependency audit or penetration test.- Follow-up automation/linting overlaps with Add linting, formatting checks, and lightweight security checks to CI #195; this issue is for the specific security fixes above.
Activity
giscebot commentedon Nov 7, 2026
Implemented in #340 (
f02e243).The PR adds bounded/versioned dashboard sessions, request-time allowlist and admin-demotion checks, logout, fail-closed same-origin validation for cookie mutations, incremental webhook body limiting, and the matching nginx limit. Local validation is green: 713 Python tests, 79 dashboard tests, and a production dashboard build.
GitHub CI is also green on the dashboard job and Python 3.11/3.12.
For MCP I made the trust decision explicit instead of inventing an ACL from job authorship: knowledge remains deployment-global, consistent with the dashboard read model, while the linked user is now documented and labelled as a lifecycle owner only. Real repository isolation would need one ACL enforced by both dashboard and MCP.
Operational caveats are recorded in the PR: existing legacy cookies will require re-login, and the nginx cap is not active until the proxy configuration is deployed.