Skip to content

Commit 13dad4a

Browse files
committed
test(sdk-core): add consolidate unspent management spoofability test for utxo coins
Ticket: WP-5945
1 parent 7854ed3 commit 13dad4a

File tree

1 file changed

+70
-0
lines changed
  • modules/sdk-coin-btc/test/unit

1 file changed

+70
-0
lines changed

modules/sdk-coin-btc/test/unit/btc.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,74 @@ describe('BTC:', () => {
106106
);
107107
});
108108
});
109+
110+
describe('Unspent management spoofability (BUILD_SIGN_SEND)', () => {
111+
let coin: Tbtc;
112+
let bitgoTest: TestBitGoAPI;
113+
before(() => {
114+
bitgoTest = TestBitGo.decorate(BitGoAPI, { env: 'test' });
115+
bitgoTest.safeRegister('tbtc', Tbtc.createInstance);
116+
bitgoTest.initializeTestVars();
117+
coin = bitgoTest.coin('tbtc') as Tbtc;
118+
});
119+
120+
it('should detect hex spoofing in BUILD_SIGN_SEND', async (): Promise<void> => {
121+
const { getDefaultWalletKeys, toKeychainObjects } = require('../../../bitgo/test/v2/unit/coins/utxo/util/keychains');
122+
const rootWalletKey = getDefaultWalletKeys();
123+
const keysObj = toKeychainObjects(rootWalletKey, 'pass');
124+
125+
const { Wallet } = await import('@bitgo/sdk-core');
126+
const wallet = new Wallet(bitgoTest, coin, {
127+
id: '5b34252f1bf349930e34020a',
128+
coin: 'tbtc',
129+
keys: keysObj.map((k) => k.id),
130+
});
131+
132+
const originalPsbt = utxolib.testutil.constructPsbt(
133+
[{ scriptType: 'p2wsh' as const, value: BigInt(10000) }],
134+
[{ address: 'tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7', value: BigInt(9000) }],
135+
coin.network,
136+
rootWalletKey,
137+
'unsigned' as const
138+
);
139+
utxolib.bitgo.addXpubsToPsbt(originalPsbt, rootWalletKey);
140+
141+
const spoofedPsbt = utxolib.testutil.constructPsbt(
142+
[{ scriptType: 'p2wsh' as const, value: BigInt(10000) }],
143+
[{ address: 'tb1pjgg9ty3s2ztp60v6lhgrw76f7hxydzuk9t9mjsndh3p2gf2ah7gs4850kn', value: BigInt(9000) }],
144+
coin.network,
145+
rootWalletKey,
146+
'unsigned' as const
147+
);
148+
utxolib.bitgo.addXpubsToPsbt(spoofedPsbt, rootWalletKey);
149+
const spoofedHex: string = spoofedPsbt.toHex();
150+
151+
const bgUrl: string = (bitgoTest as any)._baseUrl;
152+
const nock = require('nock');
153+
154+
nock(bgUrl)
155+
.post(`/api/v2/${wallet.coin()}/wallet/${wallet.id()}/consolidateUnspents`)
156+
.reply(200, { txHex: spoofedHex, consolidateId: 'test' });
157+
158+
nock(bgUrl)
159+
.post(`/api/v2/${wallet.coin()}/wallet/${wallet.id()}/tx/send`)
160+
.reply((requestBody: any) => {
161+
if (requestBody?.txHex === spoofedHex) {
162+
throw new Error('Spoofed transaction was sent: spoofing protection failed');
163+
}
164+
return [200, { txid: 'test-txid-123', status: 'signed' }];
165+
});
166+
167+
keysObj.forEach((k, i) =>
168+
nock(bgUrl).get(`/api/v2/${wallet.coin()}/key/${wallet.keyIds()[i]}`).reply(200, k)
169+
);
170+
171+
await assert.rejects(
172+
wallet.consolidateUnspents({ walletPassphrase: 'pass' }),
173+
(e: any) =>
174+
typeof e?.message === 'string' &&
175+
e.message.includes('prebuild attempts to spend to unintended external recipients')
176+
);
177+
});
178+
});
109179
});

0 commit comments

Comments
 (0)