The whole-field carve-out for allowedSites/deniedSites was false: src/background/index.js pushes an approved/denied hostname onto them in place, and the Settings revoke button filters one out in place from a different page — the exact membership-vs-leaf pattern that made a whole-field wallets diff unsafe, on a security-relevant field. A stale page's save could resurrect a just-revoked permission or wipe one just granted elsewhere. Both are now merged by address key and then by hostname (mergeSiteMap()), the same way wallets merge by identity. networkEndpoints gets the same per-key treatment for its lesser, non-security version of the same race. tokenHolderCache stays whole-field, correctly this time: nothing in src/ ever writes an entry into it. mergeListByIdentity() also had no floor of its own: two wallets sharing walletIdentity()'s empty-fallback identity collapsed into one via a Map, and mergeWallet() discarded the losing side's encryptedSecret outright when there was no shared baseline to diff against. Not reachable from today's UI, but the merge should not rely solely on call-site discipline elsewhere. A same-identity collision within `ours`, or between an unmatched `theirs` and a colliding `ours`, is now detected and both records are kept rather than one silently dropped. New tests in tests/stateMerge.test.js, confirmed failing against the prior state.js (stashed the fix, reran full suite: 3 red, 832 green; restored, all 835 green): - a dApp approval survives a stale Settings page revoking an unrelated site - a revoked site permission stays revoked against a stale page's later save - two independently created wallets with a colliding identity both survive, encryptedSecret included make check: 835/835 tests, test-verify-build 39/39, check-censored clean, lint stage ran fresh in the pinned container (not CACHED), prettier clean. No containers left running.
18 KiB
18 KiB