fix: version stored state, validate its shape, and give a corrupt blob a way out (closes #311)
Stored state had no version and no structural validation, so a corrupt blob produced a completely blank popup with no message and no recovery control, and made every dApp RPC call from every page answer a generic -32603. There was no reset or wipe control anywhere in the UI. saveState() now stamps a schema version and loadState() validates the shape. A version it does not understand, or a wallets array it cannot parse, lands on a recovery screen that names the problem, offers the stored record verbatim for export, and offers a destructive reset behind a typed confirmation. Unversioned but valid state -- which every existing install has -- migrates in place and keeps working; it is never shown a wipe prompt. A dApp call against unusable state answers -32007, which EIP-1474 leaves unassigned, rather than -32603. networkById() refuses an unknown id loudly instead of returning mainnet, and networkId is validated so a corrupt value cannot be used as an object key. Fields the gate does not refuse are floored by type, container and entries both: a malformed trackedTokens or tokenBalances entry is dropped rather than dereferenced. Verified by an independent sweep of 1152 corrupt blobs producing no blank popup, with the same harness showing 9 blanks against the previous revision.
This commit was merged in pull request #360.
This commit is contained in:
@@ -28,14 +28,26 @@
|
||||
|
||||
const { storageGet, storageSet } = require("../shared/browserApi");
|
||||
const { normalizePersisted } = require("../shared/persistedState");
|
||||
const {
|
||||
STATE_SCHEMA_VERSION,
|
||||
assertStateUsable,
|
||||
} = require("../shared/stateSchema");
|
||||
|
||||
// A fresh, fully-normalized, detached copy of the persisted profile.
|
||||
//
|
||||
// Normalized rather than raw: a legacy or malformed record is self-healed the
|
||||
// same way loadState() heals it for the popup, so the background is never the
|
||||
// one context reasoning about a shape the rest of the extension repairs.
|
||||
//
|
||||
// Throws StateUnusableError for a record this build cannot make sense of,
|
||||
// before normalization gets a chance to paper over it — the same gate, in the
|
||||
// same place, as the popup's loadState(). Every handler that consults the
|
||||
// profile comes through here, so a dApp call against such a record is answered
|
||||
// with the specific error the dispatcher maps that to (src/background/index.js)
|
||||
// rather than dereferencing its way into a generic -32603.
|
||||
async function getState() {
|
||||
const result = await storageGet("autistmask");
|
||||
assertStateUsable(result.autistmask);
|
||||
return normalizePersisted(result.autistmask);
|
||||
}
|
||||
|
||||
@@ -48,6 +60,10 @@ async function updateStateOnce(mutate) {
|
||||
const s = await getState();
|
||||
await mutate(s);
|
||||
s.hasWallet = Boolean(s.wallets && s.wallets.length > 0);
|
||||
// Stamped on every write, exactly as the popup's saveState() stamps it:
|
||||
// whichever context writes last, the record in storage is in this build's
|
||||
// shape and says so.
|
||||
s.schemaVersion = STATE_SCHEMA_VERSION;
|
||||
await storageSet({ autistmask: s });
|
||||
return s;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user