Skip to content

fix(sdk-coin-xtz): verify destination and amount in verifyTransaction#9245

Draft
bitgo-ai-agent-dev[bot] wants to merge 1 commit into
masterfrom
cshld-838-xtz-verify-transaction-destination-amount
Draft

fix(sdk-coin-xtz): verify destination and amount in verifyTransaction#9245
bitgo-ai-agent-dev[bot] wants to merge 1 commit into
masterfrom
cshld-838-xtz-verify-transaction-destination-amount

Conversation

@bitgo-ai-agent-dev

Copy link
Copy Markdown

What

  • Xtz.verifyTransaction now decodes txPrebuild.txHex and compares the encoded destination address and transfer amount against txParams.recipients[0]
  • Added rejection when the transaction cannot be decoded (fail-closed behaviour)
  • Added rejection when the prebuild output count is not exactly 1
  • No behaviour change when no recipients or no txHex are present (wallet initialisation)

Why

  • A compromised prebuild service could redirect an XTZ transfer to an attacker-controlled address while the local signer signed it silently, because verifyTransaction previously only checked that the recipient count was ≤ 1 and returned true unconditionally
  • The multisig contract validates signatures over the payload, but if the payload already encodes an attacker-chosen destination, the on-chain check does not protect the user
  • This fix ensures the locally-verified intent (destination + amount from txParams.recipients) matches the bytes that will actually be signed and broadcast

Test plan

  • should verify a valid transaction where destination and amount match — passes for a matching prebuild
  • should reject when prebuild destination does not match requested recipient — catches address redirection
  • should reject when prebuild amount does not match requested recipient — catches amount manipulation
  • should reject when the prebuild contains an unexpected number of outputs — catches unexpected multi-output builds
  • should reject when the prebuild cannot be decoded — fail-closed on corrupt/invalid txHex
  • should pass when no recipients are specified — backward-compatible for wallet-init flows
  • should pass when no txHex is present in the prebuild — backward-compatible for wallet-init flows
  • All 114 existing unit tests pass (yarn run unit-test --scope @bitgo/sdk-coin-xtz)

Ticket: CSHLD-838

Previously, Xtz.verifyTransaction only checked that the recipient
count was at most 1, then returned true unconditionally. This meant
a compromised prebuild service could redirect an XTZ transfer to an
attacker-controlled address and the local signer would still sign it.

This fix decodes the transaction from txPrebuild.txHex when a single
recipient is present and compares the encoded destination address and
amount against txParams.recipients[0]. Any decode failure or mismatch
throws, failing closed to prevent silent fund redirection.

Behavior is unchanged when no recipients or no txHex are present (e.g.
wallet initialization transactions), preserving backward compatibility.

Ticket: CSHLD-838

Co-Authored-By: Claude <noreply@anthropic.com>
Session-Id: 4815208a-ec3a-411f-a659-358c0aaa25e6
Task-Id: 0ce30aac-a75b-4b67-acca-38533b9d28f0
@linear-code

linear-code Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

CSHLD-838

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant