mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-23 11:03:41 +00:00
2aa4f141e3
The SSE events stream (`GET /api/v1/events?workspace=`) resolves the workspace via resolveWorkspace. The slug form is membership-scoped (GetWorkspacesBySlugForUser → nil → 404), but the UUID form resolves through the GLOBAL, unscoped GetWorkspaceByID. So an authenticated non-member passing another workspace's UUID reached SubscribeIfAllowed with a fully-resolved workspace and no explicit membership check. Events are still fail-closed filtered by computeSSEVisibility and the stream is torn down within ~60s by the revalidation tick, so it is not a data leak — but the open connection is a connection-slot DoS and a workspace-existence oracle (200+connected vs 404), and it diverged from the explicit membership gate enforced by RequireWorkspaceAccess and the collab sibling authorizeCollabAccess. Add an entry membership/grant gate in handleSSE for regular (non-admin) user-context callers: admit only direct workspace members or guest-grant holders; otherwise return 404 (matching the slug path — a 403 would itself be an existence oracle). Admin-via-cookie keeps its platform-wide bypass; admin-via-bearer and the legacy-token / fresh-install paths are gated by the pre-existing branches above and left untouched. Regression test asserts a non-member is rejected with 404 at the SSE entry for BOTH the slug and UUID forms (and consumes no connection slot), while a legitimate member still connects (200 + connected). Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra