privacy: wipe the local status-message mirror on logout (#204)
CI / Build & Quality Checks (push) Successful in 1m35s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 10s
CI / Trigger Desktop Build (push) Successful in 10s
CI / Playwright smoke (e2e) (push) Successful in 3m23s
CI / Build & Quality Checks (push) Successful in 1m35s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 10s
CI / Trigger Desktop Build (push) Successful in 10s
CI / Playwright smoke (e2e) (push) Successful in 3m23s
Of the two plaintext-localStorage items in #204, composer drafts were already swept on logout (#41); the presence status message + expiry were deliberately kept. They are PII with an authoritative copy in server presence, so sweep them too. The test's localStorage mock now enumerates keys like the real Storage object, so the prefix sweeps (drafts, status) are actually exercised — the old 'draft preserved' assertion only passed because Object.keys() saw nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
@@ -54,12 +54,36 @@ const clearMsgDrafts = (): void => {
|
||||
* bookmarks, user notes, status presets — themselves plaintext) → wiped by
|
||||
* `mx.clearStores()` on both logout paths
|
||||
* - the opt-in encrypted-search index (IndexedDB) → `deleteSearchCacheDatabase()`
|
||||
* - the presence status message (`lotus-status-msg-*`) is deliberately
|
||||
* preserved across a normal logout; clearing it is a separate product decision
|
||||
* - (the presence status message + expiry, `lotus-status-msg-*` /
|
||||
* `lotus-status-expiry-*`, used to be preserved; since [Gitea #204] they are
|
||||
* swept with the rest — the server-side presence status survives, so a
|
||||
* re-login loses nothing)
|
||||
* - low-sensitivity UI/metadata residue (`io.lotus.mute_timers`, collapsed
|
||||
* nav/space categories, `cinny_oidc_dynamic_clients`) is treated as
|
||||
* preferences, not swept here
|
||||
*/
|
||||
/**
|
||||
* [Gitea #204] The local mirror of the user's status message (+ its expiry) is
|
||||
* PII in plaintext; the authoritative copy lives in server presence.
|
||||
*/
|
||||
const clearStatusMessage = (): void => {
|
||||
let keys: string[];
|
||||
try {
|
||||
keys = Object.keys(localStorage);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
keys.forEach((key) => {
|
||||
if (key.startsWith('lotus-status-msg-') || key.startsWith('lotus-status-expiry-')) {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch {
|
||||
// best-effort
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const clearPlaintextCaches = (userId?: string): void => {
|
||||
clearTranslationCache();
|
||||
clearScheduledMessages();
|
||||
@@ -68,5 +92,6 @@ export const clearPlaintextCaches = (userId?: string): void => {
|
||||
clearRecentGifs();
|
||||
clearRecentStickers();
|
||||
clearMsgDrafts();
|
||||
clearStatusMessage();
|
||||
if (userId) clearNavToActivePathStore(userId);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user