fix(security): logout's search-index wipe coordinates across tabs
CI / Build & Quality Checks (push) Successful in 1m40s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 6s
CI / Playwright smoke (e2e) (push) Successful in 2m7s
CI / Build & Quality Checks (push) Successful in 1m40s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 7s
CI / Trigger Desktop Build (push) Successful in 6s
CI / Playwright smoke (e2e) (push) Successful in 2m7s
deleteSearchCacheDatabase() resolved after a 3 s "blocked" timeout while another tab still held the DB, so decrypted rows could survive logout. It now broadcasts lotus-logout first; every tab closes its handle and refuses to reopen, then the delete proceeds. A boot with no session re-runs the wipe once in case a race was still lost. Unit-tested. Fixes #45 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -6,7 +6,7 @@ import { getFallbackSession, removeFallbackSession, Session } from '../app/state
|
||||
import { LotusOidcTokenRefresher } from './oidcTokenRefresher';
|
||||
import { revokeOidcTokens } from './oidcLogout';
|
||||
import { pushSessionToSW } from '../sw-session';
|
||||
import { deleteSearchCacheDatabase } from '../app/utils/searchCache';
|
||||
import { deleteSearchCacheDatabase, shouldRunBootCleanup } from '../app/utils/searchCache';
|
||||
import { clearPlaintextCaches } from '../app/state/plaintextCaches';
|
||||
import { clearSoundboardClipCache } from '../app/utils/soundboardClips';
|
||||
|
||||
@@ -141,6 +141,21 @@ export const logoutClient = async (mx: MatrixClient) => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Gitea #45 — deleteSearchCacheDatabase()'s onblocked handling is a bounded
|
||||
// best-effort wait; if another tab held `lotus-search-cache` open through it
|
||||
// (the pre-broadcast case, or a browser without BroadcastChannel), the delete
|
||||
// stayed queued and plaintext survived on disk despite logout "succeeding".
|
||||
// Complete it on the next boot with no session, once per boot — call this
|
||||
// wherever the app decides there is no session to hydrate (e.g. alongside the
|
||||
// existing getFallbackSession() checks in src/index.tsx).
|
||||
let bootCleanupRan = false;
|
||||
export const cleanupSearchCacheIfSignedOut = async (): Promise<void> => {
|
||||
const hasSession = !!getFallbackSession();
|
||||
if (!shouldRunBootCleanup(hasSession, bootCleanupRan)) return;
|
||||
bootCleanupRan = true;
|
||||
await deleteSearchCacheDatabase();
|
||||
};
|
||||
|
||||
export const clearLoginData = async () => {
|
||||
const dbs = await window.indexedDB.databases();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user