@@ -222,4 +222,204 @@ describe('NEAR Token Enablement Validation', function () {
222222 // should pass validation and be processed successfully
223223 await basecoin . verifyTransaction ( verifyOptions ) ;
224224 } ) ;
225+
226+ /**
227+ * TEST 6: Security Test - sendTokenEnablements with Spoofed TxHex
228+ *
229+ * This test simulates what happens when the wallet platform's sendTokenEnablements
230+ * method receives a spoofed TxHex from a malicious actor. This test verifies that
231+ * our validation catches the spoofed transaction and prevents the user from being tricked.
232+ *
233+ * The test simulates the flow where:
234+ * 1. Wallet platform calls sendTokenEnablements
235+ * 2. buildTokenEnablements returns a spoofed transaction hex
236+ * 3. sendTokenEnablement calls verifyTransaction during signing
237+ * 4. verifyTransaction detects the spoofed hex and throws an error
238+ */
239+ it ( 'should throw error when sendTokenEnablements receives spoofed TxHex' , async function ( ) {
240+ // Create a mock wallet that implements the sendTokenEnablements flow
241+ const mockWallet = {
242+ id : ( ) => 'test-wallet' ,
243+ bitgo : bitgo ,
244+ baseCoin : basecoin ,
245+
246+ // Mock buildTokenEnablements to return a spoofed transaction hex
247+ buildTokenEnablements : async ( params : any ) => {
248+ return [
249+ {
250+ txHex : testData . rawTx . fungibleTokenTransfer . unsigned , // SPOOFED: This is a transfer, not storage deposit
251+ key : 'test-key' ,
252+ blockHash : 'test-block-hash' ,
253+ nonce : BigInt ( 1 ) ,
254+ txParams : {
255+ type : 'enabletoken' ,
256+ recipients : [
257+ {
258+ address : testData . accounts . account1 . address ,
259+ amount : '0' ,
260+ tokenName : 'tnear:tnep24dp' ,
261+ } ,
262+ ] ,
263+ } ,
264+ } ,
265+ ] ;
266+ } ,
267+
268+ // Mock sendTokenEnablement to simulate the signing process with verification
269+ sendTokenEnablement : async ( params : any ) => {
270+ // This is where verifyTransaction would be called during signing
271+ // The spoofed hex should cause verification to fail
272+ const verifyOptions : VerifyTransactionOptions = {
273+ txParams : params . txParams ,
274+ txPrebuild : params . prebuildTx ,
275+ wallet : mockWallet as any ,
276+ } ;
277+
278+ // This should throw an error because the hex is spoofed
279+ await basecoin . verifyTransaction ( verifyOptions ) ;
280+ return { success : true } ;
281+ } ,
282+
283+ // Implement the actual sendTokenEnablements method
284+ sendTokenEnablements : async ( params : any ) => {
285+ const unsignedBuilds = await mockWallet . buildTokenEnablements ( params ) ;
286+ const successfulTxs : any [ ] = [ ] ;
287+ const failedTxs = new Array < Error > ( ) ;
288+
289+ for ( const unsignedBuild of unsignedBuilds ) {
290+ const unsignedBuildWithOptions = {
291+ ...params ,
292+ prebuildTx : unsignedBuild ,
293+ txParams : unsignedBuild . txParams ,
294+ } ;
295+ try {
296+ const sendTx = await mockWallet . sendTokenEnablement ( unsignedBuildWithOptions ) ;
297+ successfulTxs . push ( sendTx ) ;
298+ } catch ( e ) {
299+ failedTxs . push ( e ) ;
300+ }
301+ }
302+
303+ return {
304+ success : successfulTxs ,
305+ failure : failedTxs ,
306+ } ;
307+ } ,
308+ } ;
309+
310+ // Create token enablement parameters
311+ const enableTokensParams = {
312+ enableTokens : [
313+ {
314+ name : 'tnear:tnep24dp' ,
315+ } ,
316+ ] ,
317+ } ;
318+
319+ // Call sendTokenEnablements - this should fail because of the spoofed hex
320+ const result = await mockWallet . sendTokenEnablements ( enableTokensParams ) ;
321+
322+ // The result should contain failures due to the spoofed transaction hex
323+ result . success . should . have . length ( 0 ) ;
324+ result . failure . should . have . length ( 1 ) ;
325+ result . failure [ 0 ] . message . should . containEql ( 'Storage deposit amount not matching!' ) ;
326+ } ) ;
327+
328+ /**
329+ * TEST 7: Security Test - sendAccountConsolidations with Spoofed TxHex
330+ *
331+ * This test simulates what happens when the wallet platform's sendAccountConsolidations
332+ * method receives a spoofed TxHex from a malicious actor. This test verifies that
333+ * our validation catches the spoofed transaction and prevents the user from being tricked.
334+ *
335+ * The test simulates the flow where:
336+ * 1. Wallet platform calls sendAccountConsolidations
337+ * 2. buildAccountConsolidations returns a spoofed transaction hex
338+ * 3. sendAccountConsolidation calls verifyTransaction during signing
339+ * 4. verifyTransaction detects the spoofed hex and throws an error
340+ */
341+ it ( 'should throw error when sendAccountConsolidations receives spoofed TxHex' , async function ( ) {
342+ // Create a mock wallet that implements the sendAccountConsolidations flow
343+ const mockWallet = {
344+ id : ( ) => 'test-wallet' ,
345+ bitgo : bitgo ,
346+ baseCoin : basecoin ,
347+
348+ // Mock buildAccountConsolidations to return a spoofed transaction hex
349+ buildAccountConsolidations : async ( params : any ) => {
350+ return [
351+ {
352+ txHex : testData . rawTx . fungibleTokenTransfer . unsigned , // SPOOFED: This is a transfer, not consolidation
353+ key : 'test-key' ,
354+ blockHash : 'test-block-hash' ,
355+ nonce : BigInt ( 1 ) ,
356+ txParams : {
357+ type : 'consolidate' ,
358+ recipients : [
359+ {
360+ address : testData . accounts . account1 . address ,
361+ amount : '1000000' ,
362+ } ,
363+ ] ,
364+ } ,
365+ } ,
366+ ] ;
367+ } ,
368+
369+ // Mock sendAccountConsolidation to simulate the signing process with verification
370+ sendAccountConsolidation : async ( params : any ) => {
371+ // This is where verifyTransaction would be called during signing
372+ // The spoofed hex should cause verification to fail
373+ const verifyOptions : VerifyTransactionOptions = {
374+ txParams : params . txParams ,
375+ txPrebuild : params . prebuildTx ,
376+ wallet : mockWallet as any ,
377+ } ;
378+
379+ // This should throw an error because the hex is spoofed
380+ await basecoin . verifyTransaction ( verifyOptions ) ;
381+ return { success : true } ;
382+ } ,
383+
384+ // Implement the actual sendAccountConsolidations method
385+ sendAccountConsolidations : async ( params : any ) => {
386+ const unsignedBuilds = await mockWallet . buildAccountConsolidations ( params ) ;
387+ const successfulTxs : any [ ] = [ ] ;
388+ const failedTxs = new Array < Error > ( ) ;
389+
390+ for ( const unsignedBuild of unsignedBuilds ) {
391+ const unsignedBuildWithOptions = {
392+ ...params ,
393+ prebuildTx : unsignedBuild ,
394+ txParams : unsignedBuild . txParams ,
395+ } ;
396+ try {
397+ const sendTx = await mockWallet . sendAccountConsolidation ( unsignedBuildWithOptions ) ;
398+ successfulTxs . push ( sendTx ) ;
399+ } catch ( e ) {
400+ failedTxs . push ( e ) ;
401+ }
402+ }
403+
404+ return {
405+ success : successfulTxs ,
406+ failure : failedTxs ,
407+ } ;
408+ } ,
409+ } ;
410+
411+ // Create account consolidation parameters
412+ const consolidationParams = {
413+ consolidateAddresses : [ testData . accounts . account2 . address ] ,
414+ } ;
415+
416+ // Call sendAccountConsolidations - this should fail because of the spoofed hex
417+ const result = await mockWallet . sendAccountConsolidations ( consolidationParams ) ;
418+
419+ // The result should contain failures due to the spoofed transaction hex
420+ result . success . should . have . length ( 0 ) ;
421+ result . failure . should . have . length ( 1 ) ;
422+ // The error should be related to transaction output mismatch since it's a different transaction type
423+ result . failure [ 0 ] . message . should . containEql ( 'Tx outputs does not match with expected txParams recipients' ) ;
424+ } ) ;
225425} ) ;
0 commit comments