Repository navigation
Stop any viewer from pushing a fake Pad title to live viewers #406
Copy link
Copy link
Closed
Labels
EditorTiptap & ProsemirrorTiptap & ProsemirrorSecuritySecurity, access control, and data exposureSecurity, access control, and data exposurebugSomething isn't workingSomething isn't working
Milestone
Description
Activity
- added a parent issue
on Oct 6, 2026 - added 2 commits that reference this issue
on Oct 6, 2026 - changed the title
[-][Security] Any viewer can push a fake Pad title to every live viewer through the docTitle relay[/-][+]Stop any viewer from pushing a fake Pad title to live viewers[/+]on Oct 6, 2026 - addedEditorTiptap & ProsemirrorTiptap & ProsemirrorSecuritySecurity, access control, and data exposureSecurity, access control, and data exposure
on Oct 6, 2026 Status: fixed in
6e5a537onclaude/youthful-lovelace-3nvsc6, with the recommended plan. Not merged or deployed yet.What shipped:
apps/webapp/src/utils/titleWrite.ts:onDocTitleStatelessreplacesparseDocTitlePayload. On adocTitlemessage it callsfetchDocumentand applies the REST title. It never applies the payload text. It drops the result when the user has moved to another pad.- One fetch runs at a time. Messages that arrive during a fetch cause one more fetch, 2 s later (
REFETCH_GAP_MS). So a stream of forged messages costs each viewer at most one GET every 2 s. DocTitle.tsxandMobilePadTitle.tsxnow callonDocTitleStateless. The relay, the server and the sender inuseUpdateDocMetadataare unchanged.- Jest spec:
apps/webapp/src/utils/titleWrite.test.ts. RootCLAUDE.md§Settled (Title write) andapps/hocuspocus.server/CLAUDE.mdrecord the rule.
Behaviour change: a rename reaches other viewers after one REST round trip, not at once.
Verify:
cd apps/webapp && bun run test -- src/utils/titleWrite.test.ts.Check after deploy: acceptance criteria 2 and 3 need two browsers. Rename an owned document as its owner, then rename an ownerless document as another user.
Generated by Claude Code
Metadata
Metadata
Assignees
Labels
EditorTiptap & ProsemirrorTiptap & ProsemirrorSecuritySecurity, access control, and data exposureSecurity, access control, and data exposurebugSomething isn't workingSomething isn't working
Summary
The WebSocket stateless relay forwards a
docTitlemessage from any connection to every client on the document, on every replica. It checks only the message type and size. So a signed-out or Read-only viewer can show a fake Pad title to everyone who is viewing an owned document. An example is "Session expired — sign in at ". The fake title is not saved. It lasts until reload.apps/hocuspocus.server,apps/webappWhere
apps/hocuspocus.server/src/extensions/statelessRelay.ts:13; decider at:25-39. Its comment says "The relay has no authz".apps/hocuspocus.server/src/hocuspocus.server.ts:348-365.apps/webapp/src/utils/titleWrite.ts:3-11.apps/webapp/src/components/TipTap/DocTitle.tsx:113-117andMobilePadTitle.tsx:268-273, intosettings.metadata.title.apps/hocuspocus.server/src/api/services/documents.service.ts:555-557), so the relay is the weaker path.Constraint
The Open document rule (
CONTEXT.md§Document access): a document with noownerIdmay be retitled by anyone. A server-side sender check must keep that rule. The connection context holds noownerIdtoday.Fix plan (recommended)
docTitleas a signal only. On receipt, the client refetches the title from REST (GETdocument metadata) and ignores the payload text.CLAUDE.md§Settled: the open-pad relay still runs once inuseUpdateDocMetadata'sonSuccess.Alternative: relay only when the sender may edit metadata (owner, or any user when
ownerIdis null). This needsownerIdin the connection context.Acceptance criteria
{"type":"docTitle","state":{"title":"fake"}}from a Read-only connection does not change the title any other client shows.Generated by Claude Code