fix(abstract-eth): validate defi calldata - #9583
Merged
Merged
Conversation
Contributor
ralph-bitgo
Bot
force-pushed
the
defi-614-calldata-validation
branch
3 times, most recently
from
August 27, 2026 15:11
6a7d06c to
b9bfb89
Compare
venkateshv1266
force-pushed
the
defi-614-calldata-validation
branch
from
August 28, 2026 06:35
b9bfb89 to
7b1aeb2
Compare
Decode DeFi calldata in TSS verification and compare embedded values against wallet intent; add regression coverage for redirected recipients and excess approvals. Ticket: DEFI-614 Session-Id: d90e4bb7-8f74-4cb7-aedb-aa9947b9c0ae Task-Id: 98400f52-e042-4e3a-88f1-71b74a0737bd
venkateshv1266
force-pushed
the
defi-614-calldata-validation
branch
from
August 28, 2026 06:48
7b1aeb2 to
734c375
Compare
venkateshv1266
marked this pull request as ready for review
August 28, 2026 08:13
sachinBitgo
reviewed
Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Add fail-closed calldata validation for
defiApprove,defiDeposit, anddefiWithdrawinAbstractEthLikeNewCoins.verifyTssTransaction(mirrors theestablished
WCN-495/security-fixladder pattern in the same function).For each defi intent, the client now decodes the server-built
txPrebuild.txHexcalldata and compares it against user intent before signing:
defiApprove(approve(address,uint256)=0x095ea7b3): selector match;decoded
amount==defiParams.amount.defiDeposit(deposit(uint256,address)=0x6e553f65): selector match;decoded
receiver== walletbaseAddress;assets==defiParams.amount.defiWithdraw(redeem(uint256,address,address)=0xba087652): selectormatch; decoded
receiver==baseAddressandowner==baseAddress;decoded
shares==defiParams.amount. For withdrawalsdefiParams.amountis the vault share amount to redeem (base units of the vault share token —
see
withdrawFromVault/ thedefi-withdrawintent mapping toshareTokenAmount), so it is directly comparable toredeem's first arg.Any mismatch, missing
defiParams/baseAddress, wrong selector, orundecodable/short calldata throws
TxIntentMismatchRecipientError— the clientnever returns
truefor a defi-typed tx without a successful decode + match.Why
TOB-BITGOEDMPC-2 (Trail of Bits High, parent CECHO-1905):
verifyTssTransactiontrusted the server-suppliedtxParams.typeand returnedtruefor defi types with no calldata decoded, so a compromised BitGo servercould substitute attacker calldata (e.g.
deposit(amt, attacker)redirectingfunds, or
approve(attacker, MAX_UINT256)) and the client would MPC-sign it.What is NOT done here (and why)
defiApprovespender is not client-validated. The client has no copy ofthe vault allowlist, so it cannot independently confirm the
spenderis theintended vault deposit target. This is not a gap to fill by duplicating
config — spender and vault-contract allowlisting is already enforced
server-side by Wallet Platform (
verifyDefiCalldata/validateDefiAllowlistinbitgo-microservices/.../abstractEthLikeCommon.ts). Maintaining vault configsin the SDK as a second source of truth would be redundant and drift-prone; the
authoritative allowlist stays in WP. The client-side selector + amount checks
still catch
approve(attacker, MAX_UINT256)(amount mismatch) andarbitrary-action substitution (selector mismatch).
txJson.to(the token/vault contract) is not client-validated, for thesame reason — the client has no vault config to compare it against, and WP
already allowlists it.
on
txParams.type, then validates the selector is consistent with that type.This is deliberate: the
approveselector (0x095ea7b3) is shared betweentokenApprovalanddefiApprove, andtransfer(0xa9059cbb) is sharedbetween
transferandtransferToken, so the selector alone cannotdisambiguate the user intent. Full calldata-derived intent is the parent
redesign's scope (see below).
Deferred to parent CECHO-1905
verifyTssTransactionto derive intent from parsedcalldata (a pluggable per-type decoder table) rather than trusting
txParams.type. This PR's defi branches are siblingifblocks outside therecipient gate, so they compose with that ladder and can move verbatim into a
decoder-table entry — a mechanical refactor, not a redesign.
desired (defense-in-depth beyond WP), CECHO-1905 can add an SDK→defi-service
vault-config fetch and compare
spender/txJson.toagainst it. That is anew SDK network dependency and is out of scope for a calldata-validation fix.
This PR follows the
WCN-495/security-fixpattern (same file, same function,same per-type calldata-decode-and-fail-closed ladder), so the two branches merge
cleanly.
Test plan
modules/abstract-eth—yarn lint+yarn unit-test(182 passing)modules/sdk-coin-eth—yarn lint+yarn unit-test(439 passing),including:
defiApprove,defiDeposit,defiWithdraw(real calldata)approve(attacker, MAX_UINT256),deposit(amt, attacker),redeem(shares, attacker, attacker)→ all throwTxIntentMismatchRecipientErrorshares → throw
defiParamsand short/empty calldata → fail closedTicket: DEFI-614 · Parent: CECHO-1905