Logout's search-index wipe can report success while another tab still holds the DB #45

Closed
opened 2026-09-12 01:51:11 -04:00 by jared · 0 comments
Owner

Severity: medium · Type: security · Confidence: medium

Location: src/app/utils/searchCache.ts:322-360, src/client/initMatrix.ts:120-135, src/app/pages/client/ClientRoot.tsx:160-176, src/app/hooks/useSessionSync.ts:23-34

Problem

deleteSearchCacheDatabase() handles onblocked by waiting at most 3 s and then resolving as
if the wipe happened. With a second tab open, that tab's dbPromise holds lotus-search-cache open;
the delete stays queued and logout proceeds (and reloads) regardless. The other tab is only reloaded
asynchronously by useSessionSync, and until it does, an in-flight saveRoomIndex() there can put
fresh decrypted rows — after which the queued delete may fire against a DB that was re-populated, or
the delete completes and the losing tab immediately re-creates the DB via openDb(). Net effect: the
logout guarantee ("no decrypted plaintext lingers after sign-out") is best-effort only in the
multi-tab case, which is the common case for a web client.

How to trigger

enable the search index, open two tabs, run a search in tab B, log out in tab A within the same second, then inspect indexedDB.databases()lotus-search-cache can still be present with rows.

Suggested fix

coordinate logout across tabs before wiping (BroadcastChannel "logging out" → tabs close
their DB handles and stop writing, then delete), and re-run deleteSearchCacheDatabase() on the next
boot when no session is present, rather than resolving optimistically after the timeout.


Filed from the September 2026 client audit (branch lotus @ 4bea4895).

**Severity:** medium · **Type:** security · **Confidence:** medium **Location:** `src/app/utils/searchCache.ts:322-360`, `src/client/initMatrix.ts:120-135`, `src/app/pages/client/ClientRoot.tsx:160-176`, `src/app/hooks/useSessionSync.ts:23-34` ### Problem `deleteSearchCacheDatabase()` handles `onblocked` by waiting at most 3 s and then resolving as if the wipe happened. With a second tab open, that tab's `dbPromise` holds `lotus-search-cache` open; the delete stays queued and logout proceeds (and reloads) regardless. The other tab is only reloaded asynchronously by `useSessionSync`, and until it does, an in-flight `saveRoomIndex()` there can `put` fresh decrypted rows — after which the queued delete may fire against a DB that was re-populated, or the delete completes and the losing tab immediately re-creates the DB via `openDb()`. Net effect: the logout guarantee ("no decrypted plaintext lingers after sign-out") is best-effort only in the multi-tab case, which is the common case for a web client. ### How to trigger enable the search index, open two tabs, run a search in tab B, log out in tab A within the same second, then inspect `indexedDB.databases()` — `lotus-search-cache` can still be present with rows. ### Suggested fix coordinate logout across tabs before wiping (BroadcastChannel "logging out" → tabs close their DB handles and stop writing, then delete), and re-run `deleteSearchCacheDatabase()` on the next boot when no session is present, rather than resolving optimistically after the timeout. --- _Filed from the September 2026 client audit (branch `lotus` @ 4bea4895)._
jared added this to the Audit 2026-09 · Medium & Low milestone 2026-09-12 01:51:11 -04:00
jared added the bugpriority: mediumsecurityarea: auth-session labels 2026-09-12 01:51:11 -04:00
jared self-assigned this 2026-09-12 01:51:11 -04:00
jared closed this issue 2026-09-15 21:32:09 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
2026-11-14
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#45