@@ -4,12 +4,14 @@ const { HttpsProxyAgent } = httpsProxyAgentPkg;
4
4
import * as https from "https" ;
5
5
import * as fs from "fs" ;
6
6
import config from "../config.js" ;
7
+ import { isDataUrlPayloadTooLarge } from "../lib/utils.js" ;
7
8
8
9
type RequestOptions = {
9
10
url : string ;
10
11
headers ?: Record < string , string > ;
11
12
params ?: Record < string , string | number > ;
12
13
body ?: any ;
14
+ timeout ?: number ;
13
15
raise_error ?: boolean ; // default: true
14
16
} ;
15
17
@@ -99,11 +101,53 @@ class ApiClient {
99
101
return getAxiosAgent ( ) ;
100
102
}
101
103
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
+
102
142
private async requestWrapper < T > (
103
143
fn : ( agent : AxiosRequestConfig [ "httpsAgent" ] ) => Promise < AxiosResponse < T > > ,
144
+ url : string ,
145
+ config ?: AxiosRequestConfig ,
104
146
raise_error : boolean = true ,
105
147
) : Promise < ApiResponse < T > > {
106
148
try {
149
+ this . validateUrl ( url , config ) ;
150
+
107
151
const res = await fn ( this . axiosAgent ) ;
108
152
return new ApiResponse < T > ( res ) ;
109
153
} catch ( error : any ) {
@@ -118,11 +162,19 @@ class ApiClient {
118
162
url,
119
163
headers,
120
164
params,
165
+ timeout,
121
166
raise_error = true ,
122
167
} : RequestOptions ) : Promise < ApiResponse < T > > {
168
+ const config : AxiosRequestConfig = {
169
+ headers,
170
+ params,
171
+ timeout,
172
+ httpsAgent : this . axiosAgent ,
173
+ } ;
123
174
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 ,
126
178
raise_error ,
127
179
) ;
128
180
}
@@ -131,11 +183,19 @@ class ApiClient {
131
183
url,
132
184
headers,
133
185
body,
186
+ timeout,
134
187
raise_error = true ,
135
188
} : RequestOptions ) : Promise < ApiResponse < T > > {
189
+ const config : AxiosRequestConfig = {
190
+ headers,
191
+ timeout,
192
+ httpsAgent : this . axiosAgent ,
193
+ data : body ,
194
+ } ;
136
195
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 ,
139
199
raise_error ,
140
200
) ;
141
201
}
@@ -144,11 +204,19 @@ class ApiClient {
144
204
url,
145
205
headers,
146
206
body,
207
+ timeout,
147
208
raise_error = true ,
148
209
} : RequestOptions ) : Promise < ApiResponse < T > > {
210
+ const config : AxiosRequestConfig = {
211
+ headers,
212
+ timeout,
213
+ httpsAgent : this . axiosAgent ,
214
+ data : body ,
215
+ } ;
149
216
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 ,
152
220
raise_error ,
153
221
) ;
154
222
}
@@ -157,11 +225,19 @@ class ApiClient {
157
225
url,
158
226
headers,
159
227
body,
228
+ timeout,
160
229
raise_error = true ,
161
230
} : RequestOptions ) : Promise < ApiResponse < T > > {
231
+ const config : AxiosRequestConfig = {
232
+ headers,
233
+ timeout,
234
+ httpsAgent : this . axiosAgent ,
235
+ data : body ,
236
+ } ;
162
237
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 ,
165
241
raise_error ,
166
242
) ;
167
243
}
@@ -170,11 +246,19 @@ class ApiClient {
170
246
url,
171
247
headers,
172
248
params,
249
+ timeout,
173
250
raise_error = true ,
174
251
} : RequestOptions ) : Promise < ApiResponse < T > > {
252
+ const config : AxiosRequestConfig = {
253
+ headers,
254
+ params,
255
+ timeout,
256
+ httpsAgent : this . axiosAgent ,
257
+ } ;
175
258
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 ,
178
262
raise_error ,
179
263
) ;
180
264
}
0 commit comments