Socket.IO session state and role-check callsites:
backend/open_webui/socket/main.py (lines 330-351, connect handler — role snapshotted into SESSION_POOL)backend/open_webui/socket/main.py (lines 393-398, heartbeat handler — does not refresh role)backend/open_webui/socket/main.py (line 538, ydoc:document:join — uses cached role for admin check)backend/open_webui/socket/main.py (line 611, document_save_handler — uses cached role for admin check)backend/open_webui/routers/users.py (lines 557-633, role update — does not invalidate SESSION_POOL)backend/open_webui/routers/users.py (line 641, user delete — does not invalidate SESSION_POOL)Current main branch (commit 6fdd19bf1) and likely all versions with the collaborative document (Yjs) Socket.IO handlers.
When a user connects via Socket.IO, the connect handler authenticates them via JWT and stores their user record (including role) in the in-memory SESSION_POOL dictionary keyed by session ID. The heartbeat handler keeps the session alive indefinitely but only refreshes the last_seen_at timestamp — never the role.
Role checks in the Yjs collaborative document handlers (ydoc:document:join, document_save_handler) consult the cached SESSION_POOL role rather than the database. Meanwhile, administrative role changes and user deletions do not iterate SESSION_POOL to disconnect affected sessions. As a result, a user whose admin role has been revoked retains admin privileges within their existing Socket.IO session for as long as they keep the connection alive (via automatic heartbeats).
HTTP endpoints are not affected — get_current_user at utils/auth.py refetches the user record from the database on every request. The gap is exclusive to the Socket.IO session cache.
#...
0.1.1240.1.1250.2.00.2.10.2.20.2.30.2.40.2.50.3.00.3.1+141 more0.9.0Exploitability
AV:NAC:LPR:LUI:NScope
S:UImpact
C:HI:HA:NCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N