Repository navigation
A forged presence payload can change the avatar on live chat messages #428
Description
Summary
workspace:<id> is a public Realtime topic. Any client, signed in or not, can join it, track any presence payload and send any broadcast. Clients store each payload in usersPresence under its id and trust its name and avatar. A live chat message has no user_details, so its avatar falls back to that presence entry. The media viewer takes the author name and avatar from the same place. A forged payload can therefore put a chosen avatar and name on another user's live messages until reload. No data is read.
- Severity: Low (impersonation in the UI only)
- Area: webapp chatroom
- Source: security review of 2026-10-06, finding L8
Where
- Fallback to presence:
apps/webapp/src/components/chatroom/components/MessageCard/hooks/useMessageAuthorDetails.ts:23-47(useMessageAuthorDetails). - Callers:
- Avatar:
apps/webapp/src/components/chatroom/components/MessageCard/components/MessageHeader/components/UserAvatar.tsx:13. - Media viewer:
apps/webapp/src/components/chatroom/components/MessageCard/components/MessageContent/components/MessageMediaAttachments.tsx:27-44.
- Avatar:
- Live rows carry no
user_details: theINSERThandler inapps/webapp/src/components/chatroom/hooks/useChannelRealtime.ts:297-324.mergeRowPreservingComputedColumns(:28-45) grafts it only onto rows already in memory. - Payload stored as sent:
apps/webapp/src/services/workspacePresenceSync.ts:228-240(applyPresenceBroadcast) and the presencejoinhandler at:258-266. - Public topic:
apps/webapp/src/hooks/useCatchUserPresences.ts:74-76(signed out) and:100-105(signed in). Norealtime.messagespolicy namesworkspace:.
Fix plan
- Reuse
getUserProfileForModal(userId)fromapps/webapp/src/api/users/getUserProfileForModal.ts. It already selects only public profile columns, andanoncan read them. Do not usegetUserById: itsUSER_PROFILE_COLUMNSreadstatusandonline_at, which Stop exposing every user's online status and last-active time #434 plans to revoke. - In
useMessageAuthorDetails, remove theusersPresenceread. Whenmessage.user_detailsis missing, load the author withuseQuerykeyed byuser_id, with a longstaleTime. One request then serves every message by that author. - Update the hook's comment so it names the new source.
Layer: the change stays in the chat author hook. It adds no new API function. Do not change workspacePresenceSync.ts, useCatchUserPresences.ts, or the Realtime topic. Do not split anon viewers onto another channel (apps/webapp/src/components/chatroom/CLAUDE.md §Anonymous Chat Read Path).
Out of scope
- Presence stacks, typing chips and activity chips. They stay client-asserted. Realtime presence does not bind a payload to the sender's session, so a fake face in a stack lasts until reload.
- A private
workspace:topic. Signed-out viewers sendrequestPresenceSyncon this topic (useCatchUserPresences.ts:91), so they would still need write access. Any person can also create an account. A private topic would not stop the forgery.
Acceptance criteria
- A presence or broadcast payload that reuses a message author's id does not change that message's avatar.
- The same payload does not change the author name or avatar in the media viewer.
- A live message from another user shows that user's real avatar without a reload.
- Messages loaded with
user_details(fromfetch_message_windoworfetch_messages_since) make no extra profile request. - Signed-out viewers still see who is online and on which heading.
- Signed-in presence, typing chips and heading stacks still work.
Verify
bun run --filter @docs.plus/webapp typecheckandbun run lint.- Manual: open one document as two signed-in users and one signed-out viewer. User A posts a text message and an image. Each other window shows A's real avatar on both, and A's name and avatar in the media viewer.
- In the Network tab, a burst of live messages from A makes at most one
usersrequest for A.
Related
- Stop exposing every user's online status and last-active time #434 (user directory and live activity; it changes which
userscolumns clients can read)
Generated by Claude Code
Activity
[-][Security] Anyone can fake presence on a document's workspace topic[/-][+]A forged presence payload can change the avatar on live chat messages[/+]fix(webapp): harden chat avatars, pasted nodes and new slugs
docs(repo): record the 9 Oct security and email batch in the changelo…
HMarzban commentedon Oct 9, 2026
Fixed and live.
cda1150e4bbdaae66c(production run 37976785981, green).