- Shipped
- September 23, 2026 at 11:14 AM UTC
- Author
- Kamo
- Commit
- d47b471
/topic/chat/session/{guid} and /topic/webrtc/session/{guid} were not guarded at all: any authenticated socket could SUBSCRIBE to another session's live chat messages or WebRTC offer/answer/ICE traffic purely by knowing (or guessing) its guid - the live twin of the transcript-read hole MediaController.canReadSession closed for HTTP (f7b2bd5). Client SEND to either was also open; no legitimate client ever sends to them (every real one only subscribes), so a forged SEND could inject a fake live message or signaling frame into a session the forger cannot even read. WebSocketConfig.preSend now guards SUBSCRIBE to both prefixes with SessionAccessGuard#canRead - the same rule MediaController's read gate uses, not a fourth copy of it - and **************** now refuses SEND to both outright, matching every other topic in that allowlist. canRead needed a second entry point: its existing signature asks an HttpServletRequest for the caller's rights (SUPPORT_TICKET's extra admission), and a STOMP frame has none - only what WebSocketAuthInterceptor stamped into the session at CONNECT (memberId/orgId/rights, read off the same *** session an HTTP request would have resolved). The new canRead(session, caller, List<String> rights) overload takes that shape directly; both overloads now delegate to one private implementation, so the actual rule is still written exactly once. A public chat visitor is not a Member at all - PublicChatWebSocketInterceptor stamps isPublicChat/sessionGuid instead, already resolved and verified against their session token at handshake. Their subscribe rule is a direct guid match against that, never SessionAccessGuard#canRead (which requires a Member and would refuse a caller who has none) - confirmed against kamo-internal, which only ever subscribes to these two topics and never sends to either. Covered by **************** (member admitted/refused by the guard, a public visitor admitted to their own session and refused a different one, unrelated topics untouched), new cases in SessionAccessGuardTest and StompDestinationAuthzTest for the rights-list overload and the new SEND refusal. Mutation-checked: dropping the new SUBSCRIBE check, breaking the rights-list overload's own rule, and (as a regression check) the pre-existing "unguarded" assertion this closes each turn a specific assertion red; all restored and rerun green.
