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

@linear-code

linear-code Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

CSHLD-838

@prajwalu142 prajwalu142 force-pushed the cshld-838-xtz-verify-transaction-destination-amount branch from 5ac3d86 to 415f972 Compare July 15, 2026 05:04
@prajwalu142

Copy link
Copy Markdown
Contributor

@claude review this pr for potential regressions and provide verdict

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @prajwalu142's task —— View job


Review of PR: fix(sdk-coin-xtz): verify destination and amount in verifyTransaction

Todo List:

  • Read and analyze the main implementation changes in xtz.ts
  • Review the test changes in xtz.ts test file
  • Assess potential regressions and breaking changes
  • Evaluate security improvements
  • Provide final verdict

Analysis

The PR introduces security verification logic to the verifyTransaction method in the XTZ coin implementation. The changes decode transaction hex and validate that the encoded destination address and amount match the requested recipient parameters.

Key Changes in Implementation:

  • Lines 132-164: New validation logic that decodes txPrebuild.txHex using TransactionBuilder
  • Compares decoded destination address and amount against txParams.recipients[0]
  • Fail-closed behavior: throws errors on decode failures or mismatches
  • Only activates when both recipient and txHex are present

Test Coverage:

  • 7 new comprehensive test cases covering all validation scenarios
  • Tests both positive cases (matching destination/amount) and negative cases (mismatches, decode failures)
  • Backward compatibility tests for empty recipients and missing txHex

Potential Regressions Assessment

✅ LOW RISK - The implementation follows a careful backward-compatibility approach:

  1. Conditional Activation: The new validation only runs when recipient && txPrebuild?.txHex - preserving existing behavior for wallet initialization flows
  2. Early Return Pattern: Maintains the original return true for cases without recipients or txHex
  3. Error Boundaries: Uses try-catch for transaction decoding with descriptive error messages

No Breaking Changes Identified:

  • Existing wallet initialization flows (no recipients/txHex) continue to work
  • Single recipient transactions continue to work if destination/amount match
  • Multi-recipient rejection logic remains unchanged

Security Improvements

✅ SIGNIFICANT SECURITY ENHANCEMENT:

  1. Address Redirection Protection: Prevents attackers from redirecting funds by comparing decoded destination with requested recipient
  2. Amount Manipulation Prevention: Validates that the actual transfer amount matches the requested amount
  3. Fail-Closed Design: Invalid/corrupted txHex results in rejection rather than silent approval
  4. Single Output Validation: Ensures prebuilds contain exactly one output as expected

The fix addresses a critical vulnerability where a compromised prebuild service could redirect transfers while signatures validate correctly.

Code Quality

✅ HIGH QUALITY IMPLEMENTATION:

  • Proper error handling with descriptive messages
  • Uses existing BigNumber utilities for amount comparison
  • Consistent code style with existing patterns
  • Comprehensive test coverage (86 new test lines)
  • Clear comments explaining the security logic

Verdict

🟢 APPROVED - RECOMMEND MERGE

This is a well-implemented security fix that:

  • ✅ Addresses a genuine security vulnerability
  • ✅ Maintains backward compatibility
  • ✅ Has comprehensive test coverage
  • ✅ Uses fail-closed security principles
  • ✅ No regression risk identified

The implementation is conservative, well-tested, and provides significant security value without breaking existing functionality.

Comment thread modules/sdk-coin-xtz/src/xtz.ts Outdated
Comment thread modules/sdk-coin-xtz/test/unit/xtz.ts Outdated
threw.should.equal(true);
});

it('should pass when no recipients are specified (no destination check)', async function () {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really sure if the last two cases are valid, please check and confirm

@prajwalu142 prajwalu142 force-pushed the cshld-838-xtz-verify-transaction-destination-amount branch from 415f972 to 77e948c Compare July 15, 2026 08:59
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
@prajwalu142 prajwalu142 force-pushed the cshld-838-xtz-verify-transaction-destination-amount branch from 77e948c to a3af4e4 Compare July 15, 2026 09:24
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.

2 participants