Skip to content

Commit d6a052f

Browse files
Merge pull request #5759 from BitGo/WP-3877
fix(sdk-core): validate sweep destination on utxos
2 parents e798c0d + b480b8c commit d6a052f

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as nock from 'nock';
2+
3+
import { Environments, Wallet } from '@bitgo/sdk-core';
4+
import { TestableBG, TestBitGo } from '@bitgo/sdk-test';
5+
6+
import { BitGo } from '../../../src';
7+
8+
describe('Sweep', function () {
9+
const bitgo: TestableBG & BitGo = TestBitGo.decorate(BitGo, { env: 'test' });
10+
const bgUrl: string = Environments[bitgo.getEnv()].uri;
11+
12+
before(async function () {
13+
bitgo.initializeTestVars();
14+
});
15+
16+
describe('UTXO Wallet Sweep', function () {
17+
const coin = 'tbtc';
18+
const walletId = '65f060a22df7cd8a42958441d4e90a45';
19+
const wallet = new Wallet(bitgo, bitgo.coin(coin), { id: walletId, coin, multisigType: 'on-chain' });
20+
21+
it('should validate that unsigned tx is sending funds to the appropriate destination', async function () {
22+
nock(bgUrl)
23+
.post(`/api/v2/${coin}/wallet/${walletId}/sweepWallet`)
24+
.reply(200, {
25+
txHex:
26+
// sweeping to 2N5mbsEex9Kct2xTMvosgTGFkcBCdvFgF6h
27+
'70736274ff01005301000000012e84a0661618f87d6a29b0239ed5b69782cb3ebb44a934029016d5b31a6be7de0100000000fdffffff01776c09000000000017a914896040625abcb130b3b01d7c0a3efee8dafed29a87000000004f010488b21e0000000000000000005d22e62de8d09d953462d4c03d60ee430bc8aee480f5417f4e66e4868c79209e0399d39cd40e0a03ba08a780686bc6af4dddf60afc29748f2a07f4fbec3b19e56d0468bed4264f010488b21e00000000000000000068aa84a41f6beaa95b67474c860714342b63fbcc2788fdbd77193944f8e0fd1d032c7af9680a3720d9e58e6473cc3b40bf305b210e676f60350ba7fd4fb7a3c9f6049e7b42d74f010488b21e000000000000000000f1745bc60d7085d053a69dc383dbe7b0bd165719051cd8172dd0101b0c01fb5302da5576d73fd458b0cc6111cb5471b402f83c9cef7db9ec0a9b7be2526b2588b104d9f69dee0001012b7992090000000000225120c98fbddc8c1e8975020f999bbac12eeb9916f1d727249b20dad051d7eb1f4dc8010304000000002116434a6534ea1565760d2a0c8ae658f30677b08ca3bc31c81c4495e6b327bf1c9d150068bed4260000000000000000290000000600000021165972ebee31ac0428f620b59c45156c916e44d5751f25102bec19132113b98cd815009e7b42d700000000000000002900000006000000011720918aea677d8aa2808bec883a50ca068304d93d6b650fe34e39a14b8071a9dec9011820695c95ba51589e650e8cdfa64b45ca51dd4c80556ec63e26f1fe10c2a09eb77e48fc05424954474f01c98fbddc8c1e8975020f999bbac12eeb9916f1d727249b20dad051d7eb1f4dc8918aea677d8aa2808bec883a50ca068304d93d6b650fe34e39a14b8071a9dec942035972ebee31ac0428f620b59c45156c916e44d5751f25102bec19132113b98cd803434a6534ea1565760d2a0c8ae658f30677b08ca3bc31c81c4495e6b327bf1c9d0000',
28+
txInfo: {
29+
nP2shInputs: 0,
30+
nP2shP2wshInputs: 0,
31+
nP2wshInputs: 0,
32+
nP2trKeypathInputs: 1,
33+
nP2trScriptPathLevel1Inputs: 0,
34+
nP2trScriptPathLevel2Inputs: 0,
35+
nP2shP2pkInputs: 0,
36+
outputs: {
37+
count: 1,
38+
size: 32,
39+
},
40+
txHexes: {},
41+
},
42+
feeInfo: {
43+
size: 101,
44+
fee: 9730,
45+
feeRate: 96337,
46+
feeString: '9730',
47+
payGoFee: 0,
48+
payGoFeeString: '0',
49+
},
50+
debug: {
51+
dimensions: {
52+
nP2shInputs: 0,
53+
nP2shP2wshInputs: 0,
54+
nP2wshInputs: 0,
55+
nP2trKeypathInputs: 1,
56+
nP2trScriptPathLevel1Inputs: 0,
57+
nP2trScriptPathLevel2Inputs: 0,
58+
nP2shP2pkInputs: 0,
59+
outputs: {
60+
count: 1,
61+
size: 32,
62+
},
63+
},
64+
},
65+
});
66+
67+
await wallet
68+
.sweep({ address: '2MwjK5Feadno84NqHhMY628eHeABHLE8d6U' })
69+
.should.be.rejectedWith(
70+
`invalid sweep destination 2N5mbsEex9Kct2xTMvosgTGFkcBCdvFgF6h, specified 2MwjK5Feadno84NqHhMY628eHeABHLE8d6U`
71+
);
72+
});
73+
});
74+
});

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -970,8 +970,15 @@ export class Wallet implements IWallet {
970970
]);
971971
this.bitgo.setRequestTracer(reqId);
972972
const response = await this.bitgo.post(this.url('/sweepWallet')).send(filteredParams).result();
973-
974-
// TODO(BG-3588): add txHex validation to protect man in the middle attacks replacing the txHex
973+
const transaction = await this.baseCoin.explainTransaction(response);
974+
if (transaction?.outputs.length) {
975+
const invalidOutputAddress = transaction.outputs.find((output) => output.address !== params.address);
976+
if (invalidOutputAddress) {
977+
throw new Error(`invalid sweep destination ${invalidOutputAddress.address}, specified ${params.address}`);
978+
}
979+
} else {
980+
throw new Error('invalid transaction, no destination address');
981+
}
975982

976983
const keychains = (await this.baseCoin.keychains().getKeysForSigning({ wallet: this, reqId })) as any;
977984

0 commit comments

Comments
 (0)