fix: floor malformed allowedSites, fraudContracts and selectedToken entries (closes #362)
A stored allowedSites whose value was not a list rendered a working popup and then made every subsequent save fail silently, so the user operated a wallet that persisted nothing -- worse than a blank popup, which is at least visibly broken. fraudContracts and selectedToken had the same shape: a container floored by truthiness or not at all, while its entries were dereferenced. Entries are now floored as well as containers, following the idiom #311 established, and a failed save raises a persistent banner instead of vanishing into a swallowed rejection. The per-field justifications that used to live in a hand-written header are replaced by a contract test that drives each field's hostile and falsy values through a real popup boot, so a claim about a field answers to the code rather than to prose. Its guarantee is stated narrowly and deliberately: no structural dereference on the code paths a wholly-corrupted profile takes, which is not every path a stored record takes. The paths it does not drive are named where the claim is made, and are tracked in #379.
This commit was merged in pull request #366.
This commit is contained in:
37
TODO.md
37
TODO.md
@@ -57,6 +57,43 @@ but the review is broader than any of them.
|
||||
reached a manifest but not the archive fails packaging. The artwork is
|
||||
original: a flat dark-navy rounded field with a teal triangular "A", drawn
|
||||
from geometry and rasterised into PNG, nothing traced or downloaded.
|
||||
- 2026-08-23: A persisted container whose ENTRIES were dereferenced unchecked no
|
||||
longer reaches a `.map()` or a `.toLowerCase()`
|
||||
([#362](https://git.eeqj.de/sneak/AutistMask/issues/362)). `allowedSites` was
|
||||
the worst shape available: a stored `{"0x…": "notalist"}` passed the gate,
|
||||
rendered a completely healthy popup, and then threw inside `saveState()`'s
|
||||
per-hostname merge, so every save from that moment on failed silently and the
|
||||
user went on operating a wallet that was persisting nothing — measured as
|
||||
`chrome.storage.local.set` never being called at all. `deniedSites` has the
|
||||
same shape, `fraudContracts` the same class with a milder consequence, and the
|
||||
sweep for the class turned up `selectedToken`, `rpcUrl` (handed whole to
|
||||
`new JsonRpcProvider()`, which throws synchronously outside any `try`), the
|
||||
entries of `viewData` (four restore branches gate on one truthy field and then
|
||||
dereference an address), and `selectedWallet`/`selectedAddress` (a stored
|
||||
`"map"` is TRUTHY against a real Array, so the restore guard does not
|
||||
short-circuit). All of them are now floored in `src/shared/persistedState.js`
|
||||
or refused by the per-branch guards in `src/popup/viewRouter.js`. Separately,
|
||||
a save that fails is no longer swallowed: `onSaveFailure()` in
|
||||
`src/shared/state.js` reports every failed save, awaited or not, and the popup
|
||||
raises a persistent "NOT SAVED" banner. The hand-written per-field
|
||||
justification in the header of `src/shared/stateSchema.js` — which had shipped
|
||||
a false claim in three consecutive changes — is replaced by
|
||||
`tests/persistedFieldContract.test.js`, one row per persisted field, each
|
||||
proven by driving the real code with hostile values — and, for a field whose
|
||||
only defence is that nothing dereferences it, by booting the real popup entry
|
||||
point over that value onto every view the popup can reopen onto, since that is
|
||||
the path this whole class of defect lives on. Each such field is driven at
|
||||
both polarities — a value nothing writes is wrong-typed and so truthy, so a
|
||||
falsy slot is driven too, or the field is proven unable to be falsy after the
|
||||
floor. The claim is narrow and stated as such: no structural dereference on
|
||||
the code paths a wholly-corrupted profile takes, which is not every path a
|
||||
stored record takes — a pairing of values the four slots do not produce, a
|
||||
view only forward navigation opens, anything behind a click, and everything a
|
||||
healthy profile reaches are all undriven. Within that boundary the verdict is
|
||||
unconditional, including a dereference that takes two corrupted fields at
|
||||
once, since the assertion is on the combined boot and the per-field re-boot
|
||||
can only decorate the message. A field with no row and a field that gains a
|
||||
floor while its row still claims it has none also fail `make check`.
|
||||
- 2026-08-23: A swap amount and the token it is counted in now always come from
|
||||
the same hop, on both sides of the approval screen
|
||||
([#359](https://git.eeqj.de/sneak/AutistMask/issues/359) and
|
||||
|
||||
Reference in New Issue
Block a user