Logging out (manually or by remote sign-out) leaves the open composer's draft in localStorage: the pagehide flush rewrites it after the wipe #319

Closed
opened 2026-10-05 16:20:34 -04:00 by jared · 0 comments
Owner

Both logout paths wipe plaintext caches, draft-msg-* included (clearPlaintextCaches → clearMsgDrafts, src/app/state/plaintextCaches.ts; #41 made drafts part of the wipe on purpose). Then they call window.location.reload(). The reload fires pagehide, and RoomInput's draft flush (#37, src/app/features/room/RoomInput.tsx:580-590: window.addEventListener('pagehide', flush), plus the unmount cleanup) writes the current composer content back to draft-msg-<roomId>.

Measured with a throwaway account:

  • Manual logout (Settings → Logout) with "SECRET half-typed reply" in the open room's composer: after the reload, on the login screen, localStorage["draft-msg-!ExMJ…:localhost"] = {"userId":"@aud_tmp…","nodes":[{"type":"paragraph","children":[…"SECRET half-typed reply"….
  • Remote sign-out (POST /logout/all from another device, the lost-laptop case): the client noticed, wiped, and went to the login screen. The IndexedDB stores and the outbox were gone (✔), but draft-msg-<room> still held "SECRET draft text".

The draft is decrypted text (possibly from an encrypted room). It's stored with a userId, so another account probably won't load it, but it stays on disk after logout, which is what the wipe is meant to prevent on shared or lost devices. The earlier logout-wipe check passed only because the composer was empty.

Fix (any one):

  • Set a module-level "logging out" flag before wiping, and have persistDraft/flush skip writes while it's set.
  • Or remove the pagehide listeners / unmount the client UI before clearPlaintextCaches().
  • Or wipe drafts again on the next boot when there's no session (the same place #45 finishes the search-index delete).

Test: type into the composer, log out, assert no draft-msg-* key. Also run it on the SessionLoggedOut path.


Found in the October 2026 client audit (Playwright against a local Synapse + LiveKit stack, plus code reading). Code refs are at cinny 30e2dbcd, cinny-desktop 8f67ec7, element-call 3b0050e8.

Both logout paths wipe plaintext caches, `draft-msg-*` included (`clearPlaintextCaches` → `clearMsgDrafts`, src/app/state/plaintextCaches.ts; #41 made drafts part of the wipe on purpose). Then they call `window.location.reload()`. The reload fires `pagehide`, and `RoomInput`'s draft flush (#37, src/app/features/room/RoomInput.tsx:580-590: `window.addEventListener('pagehide', flush)`, plus the unmount cleanup) **writes the current composer content back** to `draft-msg-<roomId>`. **Measured with a throwaway account:** - **Manual logout** (Settings → Logout) with "SECRET half-typed reply" in the open room's composer: after the reload, on the login screen, `localStorage["draft-msg-!ExMJ…:localhost"] = {"userId":"@aud_tmp…","nodes":[{"type":"paragraph","children":[…"SECRET half-typed reply"…`. - **Remote sign-out** (`POST /logout/all` from another device, the lost-laptop case): the client noticed, wiped, and went to the login screen. The IndexedDB stores and the outbox were gone (✔), but `draft-msg-<room>` still held "SECRET draft text". The draft is decrypted text (possibly from an encrypted room). It's stored with a `userId`, so another account probably won't *load* it, but it stays on disk after logout, which is what the wipe is meant to prevent on shared or lost devices. The earlier logout-wipe check passed only because the composer was empty. **Fix (any one):** - Set a module-level "logging out" flag before wiping, and have `persistDraft`/`flush` skip writes while it's set. - Or remove the `pagehide` listeners / unmount the client UI before `clearPlaintextCaches()`. - Or wipe drafts again on the next boot when there's no session (the same place #45 finishes the search-index delete). Test: type into the composer, log out, assert no `draft-msg-*` key. Also run it on the `SessionLoggedOut` path. --- _Found in the October 2026 client audit (Playwright against a local Synapse + LiveKit stack, plus code reading). Code refs are at cinny `30e2dbcd`, cinny-desktop `8f67ec7`, element-call `3b0050e8`._
jared added the priority: mediumsecurityarea: auth-session labels 2026-10-05 16:20:34 -04:00
jared closed this issue 2026-10-17 17:51:34 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#319