// What the floor under each persisted field actually guarantees — as a table // that RUNS, one row per field. // // This file replaces a hand-written per-field justification in the header of // src/shared/stateSchema.js. That comment shipped a false claim in three // consecutive changes: every author wrote plausible prose about thirty fields, // every reviewer re-derived it by hand, and it kept being wrong in a different // place each time. The artifact was the problem. A claim nobody can execute is // worse than no claim, because it is believed. // // So the claim is a row here instead: // // KIND.REFUSED assertStateUsable() refuses the record outright. Proven by // stateProblem() naming a problem for every hostile value. // KIND.ENTRIES normalizePersisted() floors the container AND its entries. // Proven by holds() over the normalized value. // KIND.SCALAR normalizePersisted() floors it to one scalar type, or to a // fixed fallback. Proven the same way. // KIND.LOOSE `saved.x || default`, no type check at all. The claim is // that no structural dereference of it is reachable from a // stored record — which cannot be argued, only driven, so the // proof is a boot of the REAL popup entry point over a stored // record carrying the hostile value, ONTO EVERY RESTORABLE // VIEW. Home is not where this class of defect lives. // // Every row is driven through a boot regardless of kind, but only a LOOSE row // (or a row that sets `alsoSweep`) is swept across the restore path: that is // what declaring LOOSE costs. ENTRIES and SCALAR rows are proven by their // holds() instead, because a floored value is not hostile by the time a // renderer sees it. A LOOSE row must additionally prove it is loose: if // someone floors the field — even partially — and leaves the row saying LOOSE, // the "survives verbatim" assertion fails. A field added to PERSISTED_FIELDS // with no row fails the first test in the file. // // The sweep is what makes a LOOSE row falsifiable, so read how it is driven // before trusting it. A row the ROUTER reads (`routes`) gets its own boot per // view, because a hostile value in it legitimately changes which view renders. // Every other swept field is corrupted on the SAME boot, one boot per view per // slot, and that boot has to land on the view it stored — so a field that does // move the routing cannot hide in the crowd. Every swept field is driven at // BOTH POLARITIES: a value nothing in src/ writes is a wrong-typed one and so // always truthy, which leaves `if (!state.x) { state.y.deref() }` unentered on // the very boot that corrupts x. The last slot is the falsy one for that // reason, and a field that cannot be falsy after the floor says so in its row // and is proven so. // // What that buys: a field any restorable view dereferences on that view's // render turns this file red, at either polarity. What it does not buy is a // MIX of polarities — one boot puts every swept field on the same slot, so a // branch reached only when one swept field is truthy and another falsy is not // entered. Booting every field separately at every value would be several // hundred boots and most of the suite's budget; this is forty-four. // // The three claims this replaced, all false, all caught here by construction: // rpcUrl reaching `new JsonRpcProvider()` (a synchronous throw, not a caught // request); viewData's ENTRIES being dereferenced by four restore branches // that gate on one truthy field each; and selectedWallet, where a stale // integer index is the SAFE case and `wallets["map"]` is the throwing one. const { PERSISTED_FIELDS, normalizePersisted, } = require("../src/shared/persistedState"); const { stateProblem } = require("../src/shared/stateSchema"); const { RESTORABLE_VIEWS } = require("../src/shared/restorableViews"); const { bootPopup, cleanupPopup, unversionedValidProfile, ADDRESS, TOKEN_ADDRESS, } = require("./support/popupBoot"); const KIND = { REFUSED: "refused by the gate", ENTRIES: "container and entries type-checked", SCALAR: "scalar type-checked", LOOSE: "loosely floored; safety proven by driving the popup", }; const isText = (v) => typeof v === "string"; const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v); const isIndexOrNull = (v) => v === null || (Number.isInteger(v) && v >= 0); const isTextOrNull = (v) => v === null || (isText(v) && v !== ""); const everyEntry = (v, fn) => Array.isArray(v) && v.every(fn); // A row is SWEPT — driven onto every restorable view rather than only onto // Home — when its claim is that no restore path dereferences the field. That // is what LOOSE means. The two index rows opt in with `alsoSweep` although // they are floored, because the restore path is precisely why they gained a // floor and the sweep is the regression guard on it. const swept = (row) => row.kind === KIND.LOOSE || Boolean(row.alsoSweep); // Every value a swept row drives through a boot: the hostile set, plus the // falsy slot that gives the field its other polarity. `hostile` values are all // TRUTHY by nature — a value nothing in src/ writes is a wrong-typed one, and // wrong-typed values are objects, non-empty strings and non-zero numbers. A // field that is only ever truthy on the boot that corrupts it cannot falsify // `if (!state.x) { state.y.deref() }`, so the falsy slot is not optional. const sweptValues = (row) => [...row.hostile, ...(row.falsy || [])]; // ------------------------------------------------------------------ the table // // `hostile` is values a stored record can carry that nothing in src/ ever // writes. Each one is driven through the floor AND through a real popup boot — // and, for a swept row, through one boot per restorable view — so keep the // list short and pointed. `floorOnly` is extra values checked against the // floor alone, which is pure and free. `hostileRestore` is extra values driven // through the restore path only, for a value that means nothing until a // particular branch's gate has let it past. // // `falsy` is the other POLARITY of a swept field, driven for the same reason. // It is not a value src/ never writes — for three of these fields it is the // DEFAULT_STATE default, which is the branch every ordinary install takes — // and that is the point: without it, a dereference behind `if (!state.x)` is // unreachable on the one boot that corrupts x. A swept row that cannot supply // one says `neverFalsy` instead, which is proven rather than asserted: every // falsy value stored under that field comes back TRUTHY from the floor, so no // `!state.x` branch is reachable from a stored record at all. const CONTRACT = [ { field: "wallets", kind: KIND.REFUSED, hostile: [42, "notastructure", { a: 1 }, [null], [{ addresses: 1 }]], }, { field: "networkId", kind: KIND.REFUSED, hostile: [42, "notanetwork", { a: 1 }, "__proto__"], }, { field: "trackedTokens", kind: KIND.ENTRIES, hostile: [42, "notalist", { a: 1 }], floorOnly: [[1, 2], [null], [{}], [[TOKEN_ADDRESS]]], holds: (v) => everyEntry(v, (t) => isRecord(t) && isText(t.address)), }, { field: "allowedSites", kind: KIND.ENTRIES, hostile: [42, "notarecord", { [ADDRESS]: "notalist" }], floorOnly: [ [ADDRESS], { [ADDRESS]: 42 }, { [ADDRESS]: [42, null, {}] }, JSON.parse('{"__proto__":["evil.invalid"]}'), ], holds: siteMapHolds, }, { field: "deniedSites", kind: KIND.ENTRIES, hostile: [42, "notarecord", { [ADDRESS]: "notalist" }], floorOnly: [ [ADDRESS], { [ADDRESS]: 42 }, { [ADDRESS]: [42, null, {}] }, JSON.parse('{"__proto__":["evil.invalid"]}'), ], holds: siteMapHolds, }, { field: "fraudContracts", kind: KIND.ENTRIES, hostile: [42, "notalist", { a: 1 }], floorOnly: [[42], [null], [{}], [[TOKEN_ADDRESS]]], holds: (v) => everyEntry(v, isText), }, { field: "viewStack", kind: KIND.ENTRIES, hostile: [42, "notalist", ["main", "show-phrase", "settings"]], floorOnly: [[1, 2], [null], [{}], ["export-privkey"]], // Truncated at the first entry the popup will not reopen onto, rather // than filtered: every surviving entry's Back target has to stay the // one it had. restorableStack() may also substitute ["main"] under a // view restored below the root, so this is the one ENTRIES field whose // result is not always a subset of what was stored. holds: (v) => everyEntry(v, (e) => RESTORABLE_VIEWS.has(e)), }, { field: "networkEndpoints", kind: KIND.ENTRIES, hostile: [42, "notarecord", { mainnet: "notapair" }], floorOnly: [ [1, 2], { mainnet: { rpcUrl: 42, blockscoutUrl: {} } }, { mainnet: { rpcUrl: "", blockscoutUrl: [] } }, { sepolia: 42 }, ], // Entries are coerced rather than dropped: an unknown network id is // KEPT, so a profile that has been on a build with more networks does // not lose their endpoints here. What is floored is the two URL fields // inside the pair, which applyChainSwitchFields() assigns straight onto // s.rpcUrl / s.blockscoutUrl on the next switch. holds: (v) => isRecord(v) && Object.keys(v).every((id) => { const pair = v[id]; return ( isRecord(pair) && (pair.rpcUrl === undefined || (isText(pair.rpcUrl) && pair.rpcUrl !== "")) && (pair.blockscoutUrl === undefined || (isText(pair.blockscoutUrl) && pair.blockscoutUrl !== "")) ); }), }, { field: "rpcUrl", kind: KIND.SCALAR, hostile: [42, true, { a: 1 }], floorOnly: [[], "", null], holds: (v) => isText(v) && v !== "", // The claim this row replaced said a bad value "fails the request on a // path that already catches". It does not: getProvider() hands rpcUrl // to `new JsonRpcProvider()`, which throws SYNCHRONOUSLY, from two call // sites outside any try — and a stored `currentView: "wait-tx"` reaches // one of them through restoreView(). So the row proves the claim // against the real constructor rather than describing it. alsoProven: (normalized, hostile) => { // requireActual: bootPopup() mocks this module out for the boots // above, and a mocked getProvider() would prove nothing at all // about the constructor this row is a claim about. const { getProvider } = jest.requireActual( "../src/shared/balances", ); expect(() => getProvider(hostile, "mainnet")).toThrow(); const provider = getProvider(normalized, "mainnet"); expect(provider).toBeTruthy(); provider.destroy(); }, }, { field: "blockscoutUrl", kind: KIND.SCALAR, hostile: [42, true, { a: 1 }], floorOnly: [[], "", null], holds: (v) => isText(v) && v !== "", }, { field: "activeAddress", kind: KIND.SCALAR, hostile: [42, true, { a: 1 }], floorOnly: [[ADDRESS], ""], holds: isTextOrNull, }, { field: "selectedToken", kind: KIND.SCALAR, hostile: [42, true, { a: 1 }], floorOnly: [[TOKEN_ADDRESS], ""], holds: isTextOrNull, }, { field: "selectedWallet", kind: KIND.SCALAR, // The prototype members are the whole point: `wallets["map"]` is // TRUTHY, so hasValidAddress()'s `&&` does not short-circuit and // `.addresses[…]` throws. A stale INTEGER is the safe case. hostile: ["map", "__proto__", { a: 1 }], floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true], holds: isIndexOrNull, // SCALAR, and swept anyway: the restore path is precisely why this // field gained a floor, so the sweep is the regression guard on it. alsoSweep: true, routes: true, // A stale INTEGER index, which reaches the restore path by a different // route from the prototype members above — falsy or out of range // rather than truthy — and has to keep being the safe case. hostileRestore: [{ value: "length" }, { value: 5 }], }, { field: "selectedAddress", kind: KIND.SCALAR, hostile: ["map", "__proto__", { a: 1 }], floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true], holds: isIndexOrNull, alsoSweep: true, routes: true, hostileRestore: [{ value: 5 }], }, { field: "currentView", kind: KIND.LOOSE, routes: true, // Compared, and concatenated into the debug banner's textContent // (src/popup/views/helpers.js) with no gate in front of it, which // coerces. Nothing renders FROM it without RESTORABLE_VIEWS.has() // first, and Set.has() answers false for any value. hostile: [42, "no-such-view", { a: 1 }], // `saved.currentView || null`: the falsy polarity is the popup landing // on Home, which every boot in "booting onto Home" below also drives. falsy: [""], }, { field: "viewData", kind: KIND.LOOSE, routes: true, // The container is taken verbatim; what makes its ENTRIES safe is the // per-branch guard in src/popup/viewRouter.js. The sweep drives the // container shapes below onto every restorable view; hostileRestore // adds the records that PASS a branch's gate and then hand its // renderer something it dereferences, which is where the entries are // actually decided. hostile: [42, "notarecord", { a: 1 }, [1, 2]], // `structuredClone(saved.viewData || {})`: the container is never falsy // in state whatever was stored, so no `!state.viewData` branch exists to // drive. neverFalsy: true, hostileRestore: [ // success-tx passes on `data.hash`, and renderSuccess() then calls // toAddressHtml(d.to) -> addressTitle() -> address.toLowerCase(). { value: { hash: "0x1" }, views: ["success-tx"] }, { value: { hash: "0x1", to: 42 }, views: ["success-tx"] }, { value: { hash: "0x1", to: ADDRESS, decoded: { details: 7 } }, views: ["success-tx"], }, { value: { hash: "0x1", to: ADDRESS, decoded: { details: [{ address: 42 }] }, }, views: ["success-tx"], }, // error-tx passes on `data.message`, same dereference. { value: { message: "boom" }, views: ["error-tx"] }, { value: { message: "boom", to: 42 }, views: ["error-tx"] }, // transaction passes on `data.tx`. { value: { tx: { hash: "0x1" } }, views: ["transaction"] }, { value: { tx: { hash: "0x1", from: ADDRESS, to: ADDRESS, contractAddress: 42, }, }, views: ["transaction"], }, // confirm-tx passes on `data.pendingTx`. { value: { pendingTx: { amount: "1" } }, views: ["confirm-tx"] }, { value: { pendingTx: { token: 42, from: ADDRESS, to: ADDRESS, amount: "1", }, }, views: ["confirm-tx"], }, // wait-tx passes on `pendingWait.hash`; restoreWait() has checked // the fields below it since it was written, and this is the // regression guard. { value: { pendingWait: { hash: "0x1", txInfo: { to: 42, amount: "1" }, }, }, views: ["wait-tx"], }, // A record that passes EVERY branch's gate at once, driven onto // every restorable view: a branch a view does not read must stay // one it does not read, and each renderer must survive the fields // another branch left behind. { value: { hash: "0x1", message: "boom", tx: { hash: "0x1" }, pendingTx: { amount: "1" }, pendingWait: { hash: "0x1" }, }, }, ], }, { field: "lastBalanceRefresh", kind: KIND.LOOSE, // Arithmetic only: `now - (s.lastBalanceRefresh || 0)` compares false // for a non-number and forces a refresh. hostile: [true, "notatime", { a: 1 }], // `|| 0` collapses every falsy stored value to 0, so 0 IS the whole // falsy polarity of this field — and it is the DEFAULT_STATE default, // the value a profile carries until its first refresh lands. falsy: [0], }, { field: "tokenHolderCache", kind: KIND.LOOSE, // Nothing DEREFERENCES it structurally. It is read by the // field-agnostic snapshotPersisted()/deepEqual() in // src/shared/state.js, which are safe for any value, and otherwise // only reset wholesale in src/shared/chainSwitchFields.js. hostile: [42, "notarecord", [1, 2]], // `structuredClone(saved.tokenHolderCache || {})`. neverFalsy: true, }, { field: "theme", kind: KIND.LOOSE, // Compared against "dark"/"light" in applyTheme() and otherwise falls // to the system branch; assigned into an input .value, which coerces. hostile: [42, "chartreuse", { a: 1 }], // `saved.theme || "system"`. neverFalsy: true, }, { field: "dustThresholdGwei", kind: KIND.LOOSE, hostile: ["notanumber", true, { a: 1 }], // Survives verbatim, so the falsy slot is also wrong-typed: "" reaches // filterTransactions() as a comparand and a settings input .value. falsy: [""], }, ...[ "rememberSiteChoice", "showZeroBalanceTokens", "hideSpoofedSymbols", "hideLowHolderTokens", "hideFraudContracts", "hideDustTransactions", "utcTimestamps", "debugMode", ].map((field) => ({ field, kind: KIND.LOOSE, // A flag: only ever tested for truthiness, and written back verbatim. hostile: [42, "notabool", { a: 1 }], // Both answers to that truthiness test have to be driven, and 0 is a // value src/ never writes for a flag. For utcTimestamps and debugMode // the falsy answer is also the DEFAULT_STATE default. falsy: [0], })), ]; function siteMapHolds(v) { return ( isRecord(v) && Object.getPrototypeOf(v) === Object.prototype && !Object.prototype.hasOwnProperty.call(v, "__proto__") && Object.keys(v).every((key) => everyEntry(v[key], isText)) ); } afterEach(() => { cleanupPopup(); }); // -------------------------------------------------------------- exhaustive describe("the contract covers the record", () => { test("every persisted field has exactly one row, and no row invents one", () => { const rows = CONTRACT.map((row) => row.field); expect([...rows].sort()).toEqual([...PERSISTED_FIELDS].sort()); }); test("every row declares a kind this file knows how to prove", () => { const kinds = Object.values(KIND); for (const row of CONTRACT) { expect(kinds).toContain(row.kind); expect(row.hostile.length).toBeGreaterThan(0); } }); }); // ------------------------------------------------------------- the floors function profileWith(field, value) { return unversionedValidProfile({ [field]: value }); } describe("the floor each row claims", () => { // The falsy slot is deliberately NOT in here. `saved.x || default` is a // floor on falsy values and on nothing else, so a falsy value is the one // thing a LOOSE field need not carry through verbatim; what it has to carry // through is being falsy, which "both polarities" below asserts. for (const row of CONTRACT) { const values = [...row.hostile, ...(row.floorOnly || [])]; if (row.kind === KIND.REFUSED) { test(`${row.field}: the gate refuses it`, () => { for (const value of values) { expect( typeof stateProblem(profileWith(row.field, value)), ).toBe("string"); } }); continue; } test(`${row.field}: ${row.kind}`, () => { for (const value of values) { const out = normalizePersisted(profileWith(row.field, value)); if (row.kind === KIND.LOOSE) { // The claim IS that there is no floor. A field that grows // one has to move to another kind rather than keep a row // saying its readers are what make it safe. continue; } expect({ value: value, holds: row.holds(out[row.field]), }).toEqual({ value: value, holds: true }); } }); if (row.kind === KIND.LOOSE) { test(`${row.field}: is genuinely unfloored`, () => { // EVERY value, not some: a PARTIAL floor is still a floor, and // a row that keeps saying LOOSE because one hostile value out // of three still survives is exactly the stale claim this file // exists to stop. for (const value of values) { const out = normalizePersisted( profileWith(row.field, value), ); expect({ value: value, survived: JSON.stringify(out[row.field]), }).toEqual({ value: value, survived: JSON.stringify(value), }); } }); } } }); // --------------------------------------------- driving the real popup boot // A booted popup is healthy when nothing threw out of init() and something is // on screen. A throw out of restoreView() is neither: init() does not guard it, // so the rest of popup init never runs and the user gets a popup with no view, // no message and no control on it. async function bootHealth(profile) { const env = await bootPopup(profile); return { errors: env.pageErrors, blank: env.visibleViews().length === 0, }; } const HEALTHY = { errors: [], blank: false }; // unversionedValidProfile() stores no currentView, so every boot in here lands // on Home. That is the cheap half of the proof; the restore path below is the // half that matters. // Both polarities of every swept field are driven, or the field is proven // unable to take one of them. This is the guard on the sweep itself: a hostile // set is all-truthy by construction, so without a falsy slot a dereference // behind `if (!state.x)` is never reached on the boot that corrupts x — the // same falsy-collapse blind spot the fields below were floored for. describe("both polarities of every swept field are driven", () => { const FALSY_STORED = [0, "", false, null]; const floored = (field, value) => normalizePersisted(profileWith(field, value))[field]; for (const row of CONTRACT) { if (!swept(row)) continue; if (row.neverFalsy) { test(`${row.field}: cannot be falsy in state at all`, () => { for (const value of FALSY_STORED) { expect({ stored: value, truthy: Boolean(floored(row.field, value)), }).toEqual({ stored: value, truthy: true }); } }); continue; } test(`${row.field}: truthy and falsy`, () => { // What the boots below actually drive, floored the way a renderer // sees it — not what the row says it drives. const driven = [ ...sweptValues(row), ...(row.hostileRestore || []).map((entry) => entry.value), ].map((value) => floored(row.field, value)); expect({ truthy: driven.some((value) => Boolean(value)), falsy: driven.some((value) => !value), }).toEqual({ truthy: true, falsy: true }); }); } }); describe("a hostile value for one field, booting onto Home", () => { for (const row of CONTRACT) { for (const value of sweptValues(row)) { test(`${row.field} = ${JSON.stringify(value)}`, async () => { await expect( bootHealth(profileWith(row.field, value)), ).resolves.toEqual(HEALTHY); }); } } }); describe("a row's extra proof against the real reader", () => { for (const row of CONTRACT) { if (!row.alsoProven) continue; test(row.field, () => { for (const value of row.hostile) { const out = normalizePersisted(profileWith(row.field, value)); row.alsoProven(out[row.field], value); } }); } }); // ------------------------------------------------ driving the restore path // Everything above lands on Home. Home is not where this class of defect // lives: all three of the false claims this file replaced were falsified by a // RESTORE, through the unguarded restoreView() in src/popup/index.js. So a // swept row's hostile values are driven onto EVERY restorable view, one boot // each. // // This is what makes a LOOSE row falsifiable. A field that gains a structural // dereference on any restorable view — `state.theme.toLowerCase()` in a view's // show(), say — turns the row red here, instead of waiting for a reviewer to // re-derive the claim by hand. // restoreWait() resumes from this, so it has to be a finite number and recent // enough that the resumed deadline has not already passed — a wait that has // outlived its deadline resolves on the first poll instead of staying on // screen. Read once at module load, so every boot in one run shares it. const BROADCAST_TIME = Date.now(); // A viewData well formed for every restorable branch at once, so the only // thing a swept boot can fail on is the field the row corrupts. "the base // profile the sweep corrupts" below proves this really does render each view // rather than falling back — without that, a sweep could pass by never // reaching a renderer at all. const WELL_FORMED_DATA = { hash: "0x1", message: "boom", to: ADDRESS, decoded: { details: [{ address: TOKEN_ADDRESS }] }, tx: { hash: "0x1", from: ADDRESS, to: ADDRESS, contractAddress: null }, pendingTx: { token: "ETH", from: ADDRESS, to: ADDRESS, amount: "1", balance: "2", }, pendingWait: { hash: "0x1", txInfo: { to: ADDRESS, amount: "1" }, broadcastTime: BROADCAST_TIME, }, }; function restoringOnto(view, extra) { return unversionedValidProfile({ currentView: view, selectedWallet: 0, selectedAddress: 0, selectedToken: TOKEN_ADDRESS, viewStack: ["main"], viewData: WELL_FORMED_DATA, ...extra, }); } // A boot that RESTORED is healthy and landed on the view it stored, rather // than falling back to Home — which a healthy boot also does, and which would // let a sweep pass by never running the renderer it is aimed at. async function restoredHealth(profile, view) { const env = await bootPopup(profile); return { errors: env.pageErrors, restored: env.visibleViews().includes(view), }; } const RESTORED = { errors: [], restored: true }; describe("the base profile the sweep corrupts", () => { for (const view of RESTORABLE_VIEWS) { test(`renders ${view} rather than falling back`, async () => { await expect( restoredHealth(restoringOnto(view), view), ).resolves.toEqual(RESTORED); }); } }); // A field the ROUTER itself reads — the two it gates on and the two // hasValidAddress() indexes with. A hostile value in one of these legitimately // changes which view renders, so each gets its own boot per view and is held // only to "healthy", not to "restored onto the view it stored". const routes = (row) => Boolean(row.routes); // Every routing row × every hostile value × every restorable view. Profiles // are deduplicated because a hostile `currentView` REPLACES the view being // restored onto, which would otherwise be the same boot eleven times. describe("a hostile routing value restoring onto", () => { for (const row of CONTRACT) { if (!swept(row) || !routes(row)) continue; const seen = new Set(); for (const value of sweptValues(row)) { for (const view of RESTORABLE_VIEWS) { const profile = restoringOnto(view, { [row.field]: value }); const key = JSON.stringify(profile); if (seen.has(key)) continue; seen.add(key); test(`${view}: ${row.field} = ${JSON.stringify( value, )}`, async () => { await expect(bootHealth(profile)).resolves.toEqual(HEALTHY); }); } } } }); // Every OTHER swept field, corrupted at once, one boot per view per hostile // slot: twelve fields on one boot rather than twelve boots. A field is only in // here because it is not one the router reads — and that is ASSERTED, not // argued, because the boot has to land on `view`. A field that does move the // routing turns this red and has to declare `routes` and take the individual // sweep above. // // Combining does hide one thing, and the last slot is what stops it. A hostile // value is wrong-typed and therefore TRUTHY, so on a boot where every swept // field is hostile, no `if (!state.x)` branch is entered — and a dereference // inside such a branch would go unseen however loudly it throws. The last slot // is the falsy one: every swept field that CAN be falsy is falsy on it, which // is also the state an ordinary install boots in for three of them, while the // fields that cannot be falsy stay hostile. Beyond that, a throw fails the boot // whichever field threw, and a renderer that never ran is what `restored` // forbids. When it does go red, the same view is re-booted one field at a time // so the failure names the fields rather than leaving a reader to bisect twelve // of them. const UNROUTED = CONTRACT.filter((row) => swept(row) && !routes(row)); const HOSTILE_SLOTS = Math.max( ...UNROUTED.map((row) => sweptValues(row).length), ); function unroutedValues(slot) { const fields = {}; for (const row of UNROUTED) { const values = sweptValues(row); fields[row.field] = values[slot % values.length]; } return fields; } describe("every field the router does not read, corrupted at once, onto", () => { for (const view of RESTORABLE_VIEWS) { for (let slot = 0; slot < HOSTILE_SLOTS; slot++) { test(`${view}: hostile value ${slot + 1} in all ${ UNROUTED.length } of them`, async () => { const fields = unroutedValues(slot); const together = await restoredHealth( restoringOnto(view, fields), view, ); if (together.errors.length === 0 && together.restored) { expect(together).toEqual(RESTORED); return; } const named = []; for (const row of UNROUTED) { const one = await restoredHealth( restoringOnto(view, { [row.field]: fields[row.field], }), view, ); if (one.errors.length === 0 && one.restored) continue; named.push( `${row.field}=${JSON.stringify(fields[row.field])}: ` + (one.errors.join("; ") || `fell off ${view}`), ); } expect({ view: view, fields: named }).toEqual({ view: view, fields: [], }); }); } } }); // The values that only mean something on the restore path: a viewData that // PASSES a branch's gate and then hands its renderer something dereferenced, // and the index values whose route through hasValidAddress() differs from the // row's own hostile set. describe("a restore-only hostile value onto", () => { for (const row of CONTRACT) { for (const entry of row.hostileRestore || []) { for (const view of entry.views || RESTORABLE_VIEWS) { test(`${view}: ${row.field} = ${JSON.stringify( entry.value, )}`, async () => { await expect( bootHealth( restoringOnto(view, { [row.field]: entry.value }), ), ).resolves.toEqual(HEALTHY); }); } } } });