1- import { createMockFetch , loggerMock } from '@sim/testing'
1+ import { createMockResponse , loggerMock } from '@sim/testing'
22import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
3+ import {
4+ secureFetchWithPinnedIP ,
5+ validateUrlWithDNS ,
6+ } from '@/lib/core/security/input-validation.server'
37import { transformTable } from '@/tools/shared/table'
48import type { ToolConfig } from '@/tools/types'
59import {
@@ -12,6 +16,10 @@ import {
1216import { executeRequest } from '@/tools/utils.server'
1317
1418vi . mock ( '@sim/logger' , ( ) => loggerMock )
19+ vi . mock ( '@/lib/core/security/input-validation.server' , ( ) => ( {
20+ validateUrlWithDNS : vi . fn ( ) ,
21+ secureFetchWithPinnedIP : vi . fn ( ) ,
22+ } ) )
1523
1624vi . mock ( '@/stores/settings/environment' , ( ) => {
1725 const mockStore = {
@@ -406,11 +414,18 @@ describe('validateRequiredParametersAfterMerge', () => {
406414
407415describe ( 'executeRequest' , ( ) => {
408416 let mockTool : ToolConfig
409- let mockFetch : ReturnType < typeof createMockFetch >
417+ const mockValidateUrlWithDNS = vi . mocked ( validateUrlWithDNS )
418+ const mockSecureFetchWithPinnedIP = vi . mocked ( secureFetchWithPinnedIP )
410419
411420 beforeEach ( ( ) => {
412- mockFetch = createMockFetch ( { json : { result : 'success' } , status : 200 } )
413- global . fetch = mockFetch
421+ mockValidateUrlWithDNS . mockResolvedValue ( {
422+ isValid : true ,
423+ resolvedIP : '93.184.216.34' ,
424+ originalHostname : 'api.example.com' ,
425+ } )
426+ mockSecureFetchWithPinnedIP . mockResolvedValue (
427+ createMockResponse ( { json : { result : 'success' } , status : 200 } )
428+ )
414429
415430 mockTool = {
416431 id : 'test-tool' ,
@@ -441,11 +456,15 @@ describe('executeRequest', () => {
441456 headers : { } ,
442457 } )
443458
444- expect ( mockFetch ) . toHaveBeenCalledWith ( 'https://api.example.com' , {
445- method : 'GET' ,
446- headers : { } ,
447- body : undefined ,
448- } )
459+ expect ( mockSecureFetchWithPinnedIP ) . toHaveBeenCalledWith (
460+ 'https://api.example.com' ,
461+ '93.184.216.34' ,
462+ {
463+ method : 'GET' ,
464+ headers : { } ,
465+ body : undefined ,
466+ }
467+ )
449468 expect ( mockTool . transformResponse ) . toHaveBeenCalled ( )
450469 expect ( result ) . toEqual ( {
451470 success : true ,
@@ -455,8 +474,6 @@ describe('executeRequest', () => {
455474
456475 it . concurrent ( 'should use default transform response if not provided' , async ( ) => {
457476 mockTool . transformResponse = undefined
458- const localMockFetch = createMockFetch ( { json : { result : 'success' } , status : 200 } )
459- global . fetch = localMockFetch
460477
461478 const result = await executeRequest ( 'test-tool' , mockTool , {
462479 url : 'https://api.example.com' ,
@@ -471,13 +488,14 @@ describe('executeRequest', () => {
471488 } )
472489
473490 it ( 'should handle error responses' , async ( ) => {
474- const errorFetch = createMockFetch ( {
475- ok : false ,
476- status : 400 ,
477- statusText : 'Bad Request' ,
478- json : { message : 'Invalid input' } ,
479- } )
480- global . fetch = errorFetch
491+ mockSecureFetchWithPinnedIP . mockResolvedValueOnce (
492+ createMockResponse ( {
493+ ok : false ,
494+ status : 400 ,
495+ statusText : 'Bad Request' ,
496+ json : { message : 'Invalid input' } ,
497+ } )
498+ )
481499
482500 const result = await executeRequest ( 'test-tool' , mockTool , {
483501 url : 'https://api.example.com' ,
@@ -493,8 +511,7 @@ describe('executeRequest', () => {
493511 } )
494512
495513 it . concurrent ( 'should handle network errors' , async ( ) => {
496- const errorFetch = vi . fn ( ) . mockRejectedValueOnce ( new Error ( 'Network error' ) )
497- global . fetch = errorFetch
514+ mockSecureFetchWithPinnedIP . mockRejectedValueOnce ( new Error ( 'Network error' ) )
498515
499516 const result = await executeRequest ( 'test-tool' , mockTool , {
500517 url : 'https://api.example.com' ,
@@ -510,15 +527,16 @@ describe('executeRequest', () => {
510527 } )
511528
512529 it ( 'should handle JSON parse errors in error response' , async ( ) => {
513- const errorFetch = vi . fn ( ) . mockResolvedValueOnce ( {
530+ const errorResponse = createMockResponse ( {
514531 ok : false ,
515532 status : 500 ,
516533 statusText : 'Server Error' ,
517- json : async ( ) => {
518- throw new Error ( 'Invalid JSON' )
519- } ,
520534 } )
521- global . fetch = errorFetch
535+ errorResponse . json = vi . fn ( async ( ) => {
536+ throw new Error ( 'Invalid JSON' )
537+ } )
538+ errorResponse . text = vi . fn ( async ( ) => '' )
539+ mockSecureFetchWithPinnedIP . mockResolvedValueOnce ( errorResponse )
522540
523541 const result = await executeRequest ( 'test-tool' , mockTool , {
524542 url : 'https://api.example.com' ,
@@ -548,11 +566,12 @@ describe('executeRequest', () => {
548566 } ,
549567 }
550568
551- const xmlFetch = createMockFetch ( {
552- status : 200 ,
553- text : '<xml><test>Mock XML response</test></xml>' ,
554- } )
555- global . fetch = xmlFetch
569+ mockSecureFetchWithPinnedIP . mockResolvedValueOnce (
570+ createMockResponse ( {
571+ status : 200 ,
572+ text : '<xml><test>Mock XML response</test></xml>' ,
573+ } )
574+ )
556575
557576 const result = await executeRequest ( 'test-tool' , toolWithTransform , {
558577 url : 'https://api.example.com' ,
0 commit comments