Filter predicate for the statusUpdates subscription. Exported so it can be unit-tested in
isolation (it is the security-critical gate). A push reaches a subscriber only when BOTH hold:
the push's sessionId matches the subscriber's requested sessionId (tab routing), or is '*', AND
the push's ownerUserId matches the owner key (StatusUpdateOwnerKey) of the subscriber
CONNECTION's authenticated identity (context.userPayload.userRecord, established once at WS
connect): the user ID for a named user, compared as a UUID; for an anonymous visitor, the user ID
plus its scope identity, compared exactly because the scope part is case-significant.
Condition (2) is the fix for the session-hijack class (B49): sessionId is a client-chosen
correlation value, so a subscriber who lifts another user's sessionId would previously receive
their pushes. Binding delivery to the connection's server-authenticated identity means knowing a
sessionId is no longer sufficient; the scope part does the same between anonymous visitors, who all
share one Anonymous user. Fails CLOSED — a missing owner or connection key never matches.
Filter predicate for the
statusUpdatessubscription. Exported so it can be unit-tested in isolation (it is the security-critical gate). A push reaches a subscriber only when BOTH hold:sessionIdmatches the subscriber's requestedsessionId(tab routing), or is'*', ANDownerUserIdmatches the owner key (StatusUpdateOwnerKey) of the subscriber CONNECTION's authenticated identity (context.userPayload.userRecord, established once at WS connect): the user ID for a named user, compared as a UUID; for an anonymous visitor, the user ID plus its scope identity, compared exactly because the scope part is case-significant.Condition (2) is the fix for the session-hijack class (B49):
sessionIdis a client-chosen correlation value, so a subscriber who lifts another user'ssessionIdwould previously receive their pushes. Binding delivery to the connection's server-authenticated identity means knowing asessionIdis no longer sufficient; the scope part does the same between anonymous visitors, who all share one Anonymous user. Fails CLOSED — a missing owner or connection key never matches.