Skip to content

🔒️ Only accept presence for the doc on its channel - #752

Open
alecgibson wants to merge 1 commit into
mainfrom
check-doc-presence-channel
Open

alecgibson wants to merge 1 commit into
mainfrom
check-doc-presence-channel

Conversation

@alecgibson

@alecgibson alecgibson commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #738

At the moment, the server checks read access against a presence's c and d, but broadcasts it on its ch:

  1. Deny docs/secret in readSnapshots
  2. Send presence on docs.secret with the c/d of a readable doc
  3. docs/secret subscribers apply it

This change rejects doc presence whose c and d don't match its channel, and DocPresence ignores presence for any other doc, which also covers untyped presence and dotted-name channel collisions. Forwarded sendPresence errors no longer reach a local presence with the same ID either: unlike replies to the client's own submissions, they carry no pv. Subscribing, write access and presence ID ownership are left to middleware, which the new "Access control" docs cover.

The server check only holds once every instance is upgraded, and the client filter only protects upgraded clients.

🤖 Generated with Claude Code

Co-Authored-By: Claude noreply@anthropic.com

@coveralls

coveralls commented Oct 6, 2026 •

Copy link
Copy Markdown

Coverage Status

coverage: 97.949% (+0.03%) from 97.916% — check-doc-presence-channel into main

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mismatched presence errors can bypass the client filter and reach another local presence with the same ID.

1 open finding
What changed in this PR

Hardens document presence channel validation and documents presence access-control responsibilities.

Changes:

  • Validates document presence channels server-side and filters mismatched client updates.
  • Adds security regression tests.
  • Documents middleware-based access control and presence ID ownership.
File Description
lib/​agent.js Validates document presence channels.
lib/​client/​presence/​doc-presence.js Filters updates for other documents.
test/​client/​presence/​doc-presence.js Tests channel validation and filtering.
docs/​presence.md Adds access-control guidance.
docs/​middleware/​actions.md Adds presence permission warnings.
docs/​api/​presence.md Documents presence ID ownership.
docs/​api/​backend.md Documents presence error-forwarding configuration.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/client/presence/doc-presence.js
@alecgibson
alecgibson force-pushed the check-doc-presence-channel branch from 9014faf to 798c1f2 Compare October 13, 2026 16:35
Fixes #738

At the moment, the server checks read access against a presence's `c`
and `d`, but broadcasts it on its `ch`:

 1. Deny `docs/secret` in `readSnapshots`
 2. Send presence on `docs.secret` with the `c`/`d` of a readable doc
 3. `docs/secret` subscribers apply it

This change rejects doc presence whose `c` and `d` don't match its
channel, and `DocPresence` ignores presence for any other doc, which
also covers untyped presence and dotted-name channel collisions.
Forwarded `sendPresence` errors no longer reach a local presence with
the same ID either: unlike replies to the client's own submissions,
they carry no `pv`. Subscribing, write access and presence ID
ownership are left to middleware, which the new "Access control" docs
cover.

The server check only holds once every instance is upgraded, and the
client filter only protects upgraded clients.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The implementation, tests, and documentation consistently address the reported presence-routing vulnerabilities.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

alecgibson added a commit that referenced this pull request Oct 14, 2026
Fixes #735

At the moment, the server relays a doc presence whose `v` is ahead of
the doc unchanged, which lets one client make every subscriber fetch:

 1. Submit presence with `v: 1e9` to a doc with 20 subscribers
 2. Each subscriber calls `doc.fetch()`, since `v > doc.version`
 3. The presence stays pending, so every later op fetches again

This change has the `Agent` pass its tracked doc version through to the
presence transform, which reads ops from the lower of that and `v`. The
tracker can lag the database but never lead it, so a `v` past the last
op read is ahead of the doc. Ops in that lag may have been deleted,
though, so we check the snapshot version before rejecting. Doc presence
must now carry an integer `v`, and so must ops that send one. A rejected
presence restores the `Agent`'s previously cached request, so the leave
it replays on disconnect isn't rejected too.

Presence sent without `c`/`d` skips all of this, since the server can't
tell it from generic presence, but clients ignore it since #752. We
deliberately don't bound presence that is far *behind*: a `fetch` with
`v: 0` already makes the server read the whole op history.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@alecgibson
alecgibson marked this pull request as ready for review October 15, 2026 14:07

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Document that presence has no built-in access control (eavesdrop/inject/hijack via channels)

3 participants