fix(crypto): migrate trashed entries' usernames too (§1.3)
migrateUsernamesAtRest only swept state.entries (live rows), so a soft-deleted entry kept its cleartext username in the DB until purge. Now it also fetches + decrypts the trash (GET /entries?deleted=1) and includes those rows in the sweep. The PUT updates the row's fields without touching `deleted`, so the entry stays in the trash; trashed rows aren't in the sync snapshot, so no churn. Surfaced by a lingering cleartext username on a trashed test entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1509,7 +1509,20 @@ async function withEncryptedUsername(obj) {
|
||||
// so other devices converge to the same value).
|
||||
async function migrateUsernamesAtRest() {
|
||||
if (!state.cryptoKey) return;
|
||||
const todo = state.entries.filter(e => e && !e.username_enc && (e.username || '') !== '');
|
||||
let pool = (state.entries || []).slice();
|
||||
// Trashed rows live in state.trashed (loaded on demand), not state.entries,
|
||||
// so the live-only sweep would leave a soft-deleted entry's username in
|
||||
// cleartext until purge. Fetch + decrypt the trash so it's covered too —
|
||||
// the PUT updates the row's fields without touching `deleted`, so it stays
|
||||
// in the trash. Trashed rows aren't in the sync snapshot, so no churn.
|
||||
try {
|
||||
const trash = await api('/entries?deleted=1', { headers: authHeaders() });
|
||||
if (Array.isArray(trash)) {
|
||||
await decryptEntryUsernames(trash);
|
||||
pool = pool.concat(trash);
|
||||
}
|
||||
} catch (_) {}
|
||||
const todo = pool.filter(e => e && !e.username_enc && (e.username || '') !== '');
|
||||
if (todo.length === 0) return;
|
||||
let migrated = 0;
|
||||
for (const e of todo) {
|
||||
|
||||
Reference in New Issue
Block a user