fix: gate the chain switch and remember endpoints per network (closes #308)
All checks were successful
check / check (push) Successful in 28s
e2e / e2e-chrome (push) Successful in 50s
e2e / e2e-firefox (push) Successful in 22s

wallet_switchEthereumChain was answered for any origin at all, with no
connection check and no prompt, so a page the user had never connected to
could move the active chain — clearing the [TESTNET] banner under someone
who believed they were on Sepolia. It now takes the same
allowedSites/connectedSites gate the signing methods take, ahead of the
same-chain and unsupported-chain answers, and refuses an unconnected origin
with 4100.

The switch also overwrote state.rpcUrl and state.blockscoutUrl with the
network defaults, so a user running their own node lost that url
permanently and silently to a public endpoint that then sees every address
they hold. Endpoints are now remembered per network in
state.networkEndpoints: the switch snapshots the network being left and
restores the network being entered, falling back to that network's
defaults. state.rpcUrl and state.blockscoutUrl remain the live endpoints of
the active network, so no reader changed; for the active network they are
authoritative and the map entry may be stale, and the snapshot is what
reconciles them. A profile written before the map existed has its stored
pair adopted for the network it was stored under, so nothing is lost on
first load.
This commit is contained in:
2026-08-20 10:10:43 +00:00
parent ff3387d8cf
commit 28d5dddb96
7 changed files with 485 additions and 4 deletions

17
TODO.md
View File

@@ -44,6 +44,23 @@ but the review is broader than any of them.
# Completed Steps
- 2026-08-20: A web page can no longer switch the wallet's chain, and switching
no longer destroys the user's endpoints
([#308](https://git.eeqj.de/sneak/AutistMask/issues/308)).
`wallet_switchEthereumChain` was answered for any origin at all, with no
connection check and no prompt: any page could clear the `[TESTNET]` banner
under a user who believed they were on Sepolia. It now takes the same
`allowedSites`/`connectedSites` gate the signing methods take, ahead of the
same-chain and unsupported-chain answers, and refuses an unconnected origin
with `4100`. The switch itself also overwrote `state.rpcUrl` and
`state.blockscoutUrl` with the network defaults, so a user running their own
node lost that url permanently and silently to a public endpoint that then
sees every address they hold. Endpoints are now remembered per network in
`state.networkEndpoints`, snapshotted from the network being left and restored
for the network being entered; `state.rpcUrl` stays the live value for the
active network, so no reader changed. A profile written before the map existed
has its stored pair adopted for the network it was stored under, and loses
nothing.
- 2026-08-17: The Settings screen is driven in a browser, and every element id
the popup looks up is checked statically. Nothing exercised Settings in the
e2e suite, and jest runs with no DOM, so the densest run of `$("...")` lookups