Commit Graph
5 Commits
Author SHA1 Message Date
sneak dfd197b93b harden: bound the total network fee by gasLimit × fee, on both send paths (closes #399)
e2e / e2e-chrome (push) Failing after 1s
e2e / e2e-firefox (push) Failing after 0s
check / check (push) Successful in 1m4s
The two per-field ceilings in approvalVerify.js were checked independently,
but the fee a validator is paid is gasLimit × fee per gas: a gas limit and a
fee each under their own ceiling still multiply to thousands of ETH, which a
gas-consuming contract really collects. assertWithinCeilings now also bounds
that product against MAX_TOTAL_FEE (1 ETH), so both callers — populating the
dApp transaction and verifying the signed artifact — refuse it with a full
sentence naming the fee and the limit.

The wallet's own send in confirmTx.js pinned no fee fields, so ethers filled
them from the node with no bound; it now populates the transaction and runs the
same check before signing, showing the same error in the confirmation screen's
reserved errors box so nothing on screen moves.

Model: opus-4-8
2026-09-21 19:11:40 +00:00
clawbot c06765ef8f fix: one transaction approval at a time, and honest copy for a nonce collision (closes #271)
check / check (push) Successful in 28s
2026-08-17 08:38:26 +02:00
clawbot 918e581ef3 harden: verify the signed transaction against what the popup displayed (closes #216)
check / check (push) Has been cancelled
Verification compared the signed artifact against the dApp's request object.
For every field the dApp omitted -- normally nonce, gas limit and all the fee
fields, since the popup filled them in -- the number the user actually read on
screen was verified by nothing, and only absolute ceilings stood behind it.

The transaction is now populated in the background before the approval window
opens, and that populated object is both what the popup displays and what the
signed artifact is verified against. Every consequential field becomes an
equality comparison; the ceilings remain as a backstop. Population failing
means no approval and no window, and the error goes to the requesting page --
earlier than before, where the same estimate failed after the password had been
typed.

The account is pinned too: `from` is compared against the address named at
approval time rather than whichever address is active at signing, so switching
accounts mid-flow refuses instead of signing from an account the approval did
not name. The message-signing path had the same defect and gets the same fix.

Nonce selection moves earlier as a consequence; the concurrent-approval case
that follows from it is tracked at #271.
2026-08-12 12:15:25 +02:00
clawbot 0a1786b406 harden: verify all approval fields and make failed signing retryable (closes #174)
check / check (push) Successful in 30s
approvalVerify now compares every field of the signed artifact against the
approval, not a subset. Transaction types are allowlisted to 0/1/2 and any
field the module does not check is refused outright, so a future transaction
type cannot smuggle consequential fields past verification -- an EIP-7702
type-4 artifact that delegates the signer's own EOA while matching every
displayed field was accepted before this change. The serialized bytes handed
to broadcastTransaction are compared against the parsed artifact, so the
guarantee covers the bytes that actually go to the node.

Signing failures in the popup are retryable again. To make that safe, an
approval is claimed synchronously before the first await and every path that
resolves or removes one goes through a single chokepoint that refuses a claimed
approval. Without it, closing the approval window, switching the active address
or a late reject would report "User rejected the request." to the dApp while
the broadcast completed -- the user then redoes the transfer at a fresh nonce
and it sends twice.

Failure copy distinguishes the stage reached, so a user is never told to start
again from the site when the first attempt may already have reached the network.
2026-08-12 11:21:02 +02:00
clawbot ad9162d057 security: decrypt and sign dApp approvals in the popup (closes #157)
check / check (push) Successful in 24s
The password no longer crosses the extension messaging boundary: the popup
decrypts and signs, and sends only the raw signed transaction or the signature.
The background re-derives the signer from the artifact and checks it against
the approval it holds before broadcasting, so it is not a blind relay.
2026-08-10 15:59:30 +02:00