You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Keep a purged document id gone after a PUT or a reconnect #407
After a document is purged, DocumentPurgeTombstone should stop it from coming back. But PUT /api/documents/:docId creates a metadata row for any id and never reads the tombstone. Once a row exists, the WebSocket gate also skips the tombstone. A stale browser tab that was offline during the purge can then reconnect and upload its local copy, so the erased content is stored again. No attacker is needed; a stale tab's title edit is enough.
Severity: Low
Area:apps/hocuspocus.server
Source: security review of 2026-10-06
Where
Route: apps/hocuspocus.server/src/api/routers/documents.router.ts:57-62 (optionalUser, so anonymous is allowed). No :docId param schema.
updateDocument checks only deletedAt (apps/hocuspocus.server/src/api/services/documents.service.ts:545-549), then upserts any id (:603-615).
WebSocket gate reads the tombstone only when no row exists: apps/hocuspocus.server/src/hocuspocus.server.ts:553-559.
What the tombstone is for: apps/hocuspocus.server/src/api/services/documentPurge.service.ts:86-90.
Fix plan
In updateDocument, when no row exists, look up DocumentPurgeTombstone for the id. If found, return 404 and create nothing.
In the WebSocket onAuthenticate, check the tombstone even when a metadata row exists, and refuse the connection.
Add a documentIdSchema param validator to the documents routes that take :docId or :documentId (update, delete, restore, duplicate). This also helps the local-storage path issue.
Before you enforce the id format, count existing rows that do not match it, so you do not lock out real documents.
Acceptance criteria
After a purge, PUT /api/documents/<purgedId> returns 404 and creates no row.
After a purge, a WebSocket connection to that room is refused, even if a row was created earlier.
Creating and updating normal documents still works, for both signed-in and signed-out users.
Verify
Add a test under apps/hocuspocus.server/src/api/services/__tests__/ that purges a document, then calls updateDocument with the same id.
Fixed in 02f7530: updateDocument returns 404 for a purged id with no row, and the WebSocket gate reads the tombstone even when a row exists. A regression case was added to tests/integration/documents.test.ts.
Not done on purpose: the documentIdSchema param validator. Production hocuspocus rows were not checked, so older ids that do not match the format cannot be ruled out. Enforcing it could lock out real documents. The tombstone check closes this issue on its own.
changed the title [-][Security] PUT /api/documents/:docId can bring back a purged document id and bypass the purge tombstone[/-][+]Keep a purged document id gone after a PUT or a reconnect[/+]on Oct 6, 2026
Only the PUT path has a test. The WebSocket gate change (acceptance criterion 2) has no automated test. Check it by hand after deploy: purge a test document, then reconnect a tab that was offline during the purge. The server must refuse the connection.
The WebSocket gate now reads the metadata row and the tombstone in parallel on every connection. Before, it read the tombstone only when no row existed.
Verify the PUT case: cd apps/hocuspocus.server && bun test tests/integration/documents.test.ts.
Activity
fix(hocuspocus): keep purged document ids gone
HMarzban commentedon Oct 6, 2026
Fixed in
02f7530:updateDocumentreturns 404 for a purged id with no row, and the WebSocket gate reads the tombstone even when a row exists. A regression case was added totests/integration/documents.test.ts.Not done on purpose: the
documentIdSchemaparam validator. Production hocuspocus rows were not checked, so older ids that do not match the format cannot be ruled out. Enforcing it could lock out real documents. The tombstone check closes this issue on its own.Generated by Claude Code
docs: record the security and push fixes in changelogs and agent docs
[-][Security] PUT /api/documents/:docId can bring back a purged document id and bypass the purge tombstone[/-][+]Keep a purged document id gone after a PUT or a reconnect[/+]HMarzban commentedon Oct 6, 2026
Two facts the comment above does not record:
PUTpath has a test. The WebSocket gate change (acceptance criterion 2) has no automated test. Check it by hand after deploy: purge a test document, then reconnect a tab that was offline during the purge. The server must refuse the connection.Verify the
PUTcase:cd apps/hocuspocus.server && bun test tests/integration/documents.test.ts.Generated by Claude Code
chore(merge): land the security and push fixes branch
refactor(hocuspocus): tidy the push, SSRF, error and admin-log follow…