@@ -4,12 +4,14 @@ const { HttpsProxyAgent } = httpsProxyAgentPkg;
44import * as https from "https" ;
55import * as fs from "fs" ;
66import config from "../config.js" ;
7+ import { isDataUrlPayloadTooLarge } from "../lib/utils.js" ;
78
89type RequestOptions = {
910 url : string ;
1011 headers ?: Record < string , string > ;
1112 params ?: Record < string , string | number > ;
1213 body ?: any ;
14+ timeout ?: number ;
1315 raise_error ?: boolean ; // default: true
1416} ;
1517
@@ -99,11 +101,53 @@ class ApiClient {
99101 return getAxiosAgent ( ) ;
100102 }
101103
104+ private validateUrl ( url : string , options ?: AxiosRequestConfig ) {
105+ try {
106+ const parsedUrl = new URL ( url ) ;
107+
108+ // Default safe limits
109+ const maxContentLength = options ?. maxContentLength ?? 20 * 1024 * 1024 ; // 20MB
110+ const maxBodyLength = options ?. maxBodyLength ?? 20 * 1024 * 1024 ; // 20MB
111+ const maxUrlLength = 8000 ; // cutoff for URLs
112+
113+ // Check overall URL length
114+ if ( url . length > maxUrlLength ) {
115+ throw new Error (
116+ `URL length exceeds maxUrlLength (${ maxUrlLength } chars)` ,
117+ ) ;
118+ }
119+
120+ if ( parsedUrl . protocol === "data:" ) {
121+ // Either reject completely OR check payload size
122+ if ( isDataUrlPayloadTooLarge ( url , maxContentLength ) ) {
123+ throw new Error ( "data: URI payload too large or invalid" ) ;
124+ }
125+ } else if ( ! [ "http:" , "https:" ] . includes ( parsedUrl . protocol ) ) {
126+ throw new Error ( `Unsupported URL scheme: ${ parsedUrl . protocol } ` ) ;
127+ }
128+
129+ if (
130+ options ?. data &&
131+ Buffer . byteLength ( JSON . stringify ( options . data ) , "utf8" ) > maxBodyLength
132+ ) {
133+ throw new Error (
134+ `Request body exceeds maxBodyLength (${ maxBodyLength } bytes)` ,
135+ ) ;
136+ }
137+ } catch ( error : any ) {
138+ throw new Error ( `Invalid URL: ${ error . message } ` ) ;
139+ }
140+ }
141+
102142 private async requestWrapper < T > (
103143 fn : ( agent : AxiosRequestConfig [ "httpsAgent" ] ) => Promise < AxiosResponse < T > > ,
144+ url : string ,
145+ config ?: AxiosRequestConfig ,
104146 raise_error : boolean = true ,
105147 ) : Promise < ApiResponse < T > > {
106148 try {
149+ this . validateUrl ( url , config ) ;
150+
107151 const res = await fn ( this . axiosAgent ) ;
108152 return new ApiResponse < T > ( res ) ;
109153 } catch ( error : any ) {
@@ -118,11 +162,19 @@ class ApiClient {
118162 url,
119163 headers,
120164 params,
165+ timeout,
121166 raise_error = true ,
122167 } : RequestOptions ) : Promise < ApiResponse < T > > {
168+ const config : AxiosRequestConfig = {
169+ headers,
170+ params,
171+ timeout,
172+ httpsAgent : this . axiosAgent ,
173+ } ;
123174 return this . requestWrapper < T > (
124- ( agent ) =>
125- this . instance . get < T > ( url , { headers, params, httpsAgent : agent } ) ,
175+ ( ) => this . instance . get < T > ( url , config ) ,
176+ url ,
177+ config ,
126178 raise_error ,
127179 ) ;
128180 }
@@ -131,11 +183,19 @@ class ApiClient {
131183 url,
132184 headers,
133185 body,
186+ timeout,
134187 raise_error = true ,
135188 } : RequestOptions ) : Promise < ApiResponse < T > > {
189+ const config : AxiosRequestConfig = {
190+ headers,
191+ timeout,
192+ httpsAgent : this . axiosAgent ,
193+ data : body ,
194+ } ;
136195 return this . requestWrapper < T > (
137- ( agent ) =>
138- this . instance . post < T > ( url , body , { headers, httpsAgent : agent } ) ,
196+ ( ) => this . instance . post < T > ( url , config . data , config ) ,
197+ url ,
198+ config ,
139199 raise_error ,
140200 ) ;
141201 }
@@ -144,11 +204,19 @@ class ApiClient {
144204 url,
145205 headers,
146206 body,
207+ timeout,
147208 raise_error = true ,
148209 } : RequestOptions ) : Promise < ApiResponse < T > > {
210+ const config : AxiosRequestConfig = {
211+ headers,
212+ timeout,
213+ httpsAgent : this . axiosAgent ,
214+ data : body ,
215+ } ;
149216 return this . requestWrapper < T > (
150- ( agent ) =>
151- this . instance . put < T > ( url , body , { headers, httpsAgent : agent } ) ,
217+ ( ) => this . instance . put < T > ( url , config . data , config ) ,
218+ url ,
219+ config ,
152220 raise_error ,
153221 ) ;
154222 }
@@ -157,11 +225,19 @@ class ApiClient {
157225 url,
158226 headers,
159227 body,
228+ timeout,
160229 raise_error = true ,
161230 } : RequestOptions ) : Promise < ApiResponse < T > > {
231+ const config : AxiosRequestConfig = {
232+ headers,
233+ timeout,
234+ httpsAgent : this . axiosAgent ,
235+ data : body ,
236+ } ;
162237 return this . requestWrapper < T > (
163- ( agent ) =>
164- this . instance . patch < T > ( url , body , { headers, httpsAgent : agent } ) ,
238+ ( ) => this . instance . patch < T > ( url , config . data , config ) ,
239+ url ,
240+ config ,
165241 raise_error ,
166242 ) ;
167243 }
@@ -170,11 +246,19 @@ class ApiClient {
170246 url,
171247 headers,
172248 params,
249+ timeout,
173250 raise_error = true ,
174251 } : RequestOptions ) : Promise < ApiResponse < T > > {
252+ const config : AxiosRequestConfig = {
253+ headers,
254+ params,
255+ timeout,
256+ httpsAgent : this . axiosAgent ,
257+ } ;
175258 return this . requestWrapper < T > (
176- ( agent ) =>
177- this . instance . delete < T > ( url , { headers, params, httpsAgent : agent } ) ,
259+ ( ) => this . instance . delete < T > ( url , config ) ,
260+ url ,
261+ config ,
178262 raise_error ,
179263 ) ;
180264 }
0 commit comments