Skip to content

A forged presence payload can change the avatar on live chat messages #428

Closed

Description

@HMarzban

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.
  • Live rows carry no user_details: the INSERT handler in apps/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 presence join handler at :258-266.
  • Public topic: apps/webapp/src/hooks/useCatchUserPresences.ts:74-76 (signed out) and :100-105 (signed in). No realtime.messages policy names workspace:.

Fix plan

  1. Reuse getUserProfileForModal(userId) from apps/webapp/src/api/users/getUserProfileForModal.ts. It already selects only public profile columns, and anon can read them. Do not use getUserById: its USER_PROFILE_COLUMNS read status and online_at, which Stop exposing every user's online status and last-active time #434 plans to revoke.
  2. In useMessageAuthorDetails, remove the usersPresence read. When message.user_details is missing, load the author with useQuery keyed by user_id, with a long staleTime. One request then serves every message by that author.
  3. 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 send requestPresenceSync on 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 (from fetch_message_window or fetch_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 typecheck and bun 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 users request for A.

Related


Generated by Claude Code

Activity

  1. added theissue type on Oct 6, 2026
  2. changed the title [-][Security] Anyone can fake presence on a document's workspace topic[/-] [+]A forged presence payload can change the avatar on live chat messages[/+] on Oct 6, 2026
  3. added
    ChatRelated to chat features
    SecuritySecurity, access control, and data exposure
    on Oct 6, 2026
  4. HMarzban commented on Oct 9, 2026

    @HMarzban
    CollaboratorAuthor

    Fixed and live.

    • Commits: cda1150e4
    • Deployed in bbdaae66c (production run 37976785981, green).
    • A live message takes its author from the public profile, never from presence.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ChatRelated to chat featuresSecuritySecurity, access control, and data exposurebugSomething isn't working

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions