@@ -16,15 +16,18 @@ vi.mock('@/lib/core/config/env-flags', () => ({
1616 getProxyUrl : ( ) => undefined ,
1717} ) )
1818
19+ import { resolveHostAddresses } from '@sim/security/dns'
1920import {
2021 DEFAULT_MAX_RESPONSE_BYTES ,
2122 secureFetchWithPinnedIP ,
2223} from '@/lib/core/security/input-validation.server'
2324
2425const servers : http . Server [ ] = [ ]
26+ const mockResolveHostAddresses = vi . mocked ( resolveHostAddresses )
2527
2628afterEach ( ( ) => {
2729 for ( const server of servers . splice ( 0 ) ) server . close ( )
30+ vi . clearAllMocks ( )
2831} )
2932
3033/** Starts a throwaway loopback server and returns its origin. */
@@ -36,6 +39,49 @@ async function startServer(handler: http.RequestListener): Promise<string> {
3639}
3740
3841describe ( 'secureFetchWithPinnedIP response cap' , ( ) => {
42+ it ( 'does not open a request when the signal is already aborted' , async ( ) => {
43+ let requests = 0
44+ const origin = await startServer ( ( _req , res ) => {
45+ requests += 1
46+ res . end ( 'unexpected' )
47+ } )
48+ const controller = new AbortController ( )
49+ controller . abort ( new Error ( 'cancelled' ) )
50+
51+ await expect (
52+ secureFetchWithPinnedIP ( origin , '127.0.0.1' , {
53+ allowHttp : true ,
54+ signal : controller . signal ,
55+ } )
56+ ) . rejects . toThrow ( 'cancelled' )
57+ expect ( requests ) . toBe ( 0 )
58+ } )
59+
60+ it ( 'does not follow a redirect when cancellation arrives during redirect DNS validation' , async ( ) => {
61+ let targetRequests = 0
62+ const targetOrigin = await startServer ( ( _req , res ) => {
63+ targetRequests += 1
64+ res . end ( 'unexpected' )
65+ } )
66+ const redirectOrigin = await startServer ( ( _req , res ) => {
67+ res . writeHead ( 302 , { Location : targetOrigin . replace ( '127.0.0.1' , 'localhost' ) } )
68+ res . end ( )
69+ } )
70+ const controller = new AbortController ( )
71+ mockResolveHostAddresses . mockImplementationOnce ( async ( ) => {
72+ controller . abort ( new Error ( 'cancelled during redirect DNS' ) )
73+ return { addresses : [ '127.0.0.1' ] , preferred : '127.0.0.1' }
74+ } )
75+
76+ await expect (
77+ secureFetchWithPinnedIP ( redirectOrigin , '127.0.0.1' , {
78+ allowHttp : true ,
79+ signal : controller . signal ,
80+ } )
81+ ) . rejects . toThrow ( 'cancelled during redirect DNS' )
82+ expect ( targetRequests ) . toBe ( 0 )
83+ } )
84+
3985 it ( 'rejects a body that exceeds an explicit cap instead of buffering it' , async ( ) => {
4086 const origin = await startServer ( ( _req , res ) => {
4187 res . writeHead ( 200 , { 'Content-Type' : 'application/octet-stream' } )
0 commit comments