fix(security): revoke soundboard blob URLs on logout; cap the cache

Decrypted clip blob: URLs lived in an unbounded module Map for the page
lifetime and survived logout. Add clearSoundboardClipCache() (called from
both logout paths next to clearPlaintextCaches) and a 64-entry LRU that
revokes on evict. Unit-tested.

Fixes #57

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-12 20:28:41 -04:00
co-authored by Claude Opus 5
parent 0f7f0100af
commit ac0ec9f42d
3 changed files with 125 additions and 2 deletions
+7
View File
@@ -8,6 +8,7 @@ import { revokeOidcTokens } from './oidcLogout';
import { pushSessionToSW } from '../sw-session';
import { deleteSearchCacheDatabase } from '../app/utils/searchCache';
import { clearPlaintextCaches } from '../app/state/plaintextCaches';
import { clearSoundboardClipCache } from '../app/utils/soundboardClips';
// Thrown when the local IndexedDB has a higher schema version than this SDK expects.
// This happens after a downgrade (e.g. matrix-js-sdk was briefly upgraded and then reverted).
@@ -103,6 +104,9 @@ export const startClient = async (mx: MatrixClient) => {
export const clearCacheAndReload = async (mx: MatrixClient) => {
mx.stopClient();
clearNavToActivePathStore(mx.getSafeUserId());
// [Gitea #57] Soundboard clip blob URLs hold decrypted media reachable for
// the whole page session — revoke them alongside the rest of the caches.
clearSoundboardClipCache();
await mx.store.deleteAllData();
window.location.reload();
};
@@ -128,6 +132,9 @@ export const logoutClient = async (mx: MatrixClient) => {
// scheduled messages, recent searches/forwards/gifs/stickers, nav paths) —
// wipe them too.
clearPlaintextCaches(mx.getUserId() ?? undefined);
// [Gitea #57] Same reasoning as clearPlaintextCaches: decrypted soundboard
// clip bytes stay reachable via live blob: URLs until the reload otherwise.
clearSoundboardClipCache();
// Remove only the session credential keys, preserving user preferences and
// unsent drafts (N98). The factory-reset path is clearLoginData() below.
removeFallbackSession();