@@ -140,7 +140,10 @@ const isValidURL = (url) => {
140
140
} catch ( error ) {
141
141
// If URL parsing fails, it might be a relative URL without protocol
142
142
// Allow it if it doesn't contain protocol indicators or suspicious patterns
143
- return ! url . includes ( '://' ) && ! url . includes ( '\\' ) && ! url . includes ( '@' )
143
+ if ( error instanceof TypeError ) {
144
+ return ! url . includes ( '://' ) && ! url . includes ( '\\' ) && ! url . includes ( '@' )
145
+ }
146
+ return false
144
147
}
145
148
}
146
149
@@ -149,6 +152,7 @@ const isAllowedHost = (hostname) => {
149
152
const allowedDomains = [
150
153
'api.contentstack.io' ,
151
154
'eu-api.contentstack.com' ,
155
+ 'au-api.contentstack.com' ,
152
156
'azure-na-api.contentstack.com' ,
153
157
'azure-eu-api.contentstack.com' ,
154
158
'gcp-na-api.contentstack.com' ,
@@ -177,14 +181,53 @@ const isAllowedHost = (hostname) => {
177
181
} )
178
182
}
179
183
184
+ // Helper function to validate individual URL properties
185
+ const validateURLProperty = ( config , prop ) => {
186
+ if ( config [ prop ] && ! isValidURL ( config [ prop ] ) ) {
187
+ throw new Error ( `SSRF Prevention: ${ prop } "${ config [ prop ] } " is not allowed` )
188
+ }
189
+ }
190
+
191
+ // Helper function to validate combined URL (baseURL + url)
192
+ const validateCombinedURL = ( baseURL , url ) => {
193
+ try {
194
+ let fullURL
195
+ // Handle relative URLs with baseURL
196
+ if ( url . startsWith ( '/' ) || url . startsWith ( './' ) || url . startsWith ( '../' ) ) {
197
+ fullURL = new URL ( url , baseURL ) . href
198
+ } else {
199
+ // If url is absolute, it overrides baseURL
200
+ fullURL = url
201
+ }
202
+
203
+ if ( ! isValidURL ( fullURL ) ) {
204
+ throw new Error ( `SSRF Prevention: Combined URL "${ fullURL } " is not allowed` )
205
+ }
206
+ } catch ( error ) {
207
+ if ( error . message . startsWith ( 'SSRF Prevention:' ) ) {
208
+ throw error
209
+ }
210
+ throw new Error ( `SSRF Prevention: Invalid URL combination of baseURL "${ baseURL } " and url "${ url } "` )
211
+ }
212
+ }
213
+
180
214
export const validateAndSanitizeConfig = ( config ) => {
181
- if ( ! config || ! config . url ) {
182
- throw new Error ( 'Invalid request configuration: missing URL' )
215
+ if ( ! config ) {
216
+ throw new Error ( 'Invalid request configuration: missing config' )
217
+ }
218
+
219
+ // Validate all possible URL properties in axios config to prevent SSRF attacks
220
+ const urlProperties = [ 'url' , 'baseURL' ]
221
+ urlProperties . forEach ( prop => validateURLProperty ( config , prop ) )
222
+
223
+ // If we have both baseURL and url, validate the combined URL
224
+ if ( config . baseURL && config . url ) {
225
+ validateCombinedURL ( config . baseURL , config . url )
183
226
}
184
227
185
- // Validate the URL to prevent SSRF attacks
186
- if ( ! isValidURL ( config . url ) ) {
187
- throw new Error ( `SSRF Prevention: URL " ${ config . url } " is not allowed` )
228
+ // Ensure we have at least one URL property
229
+ if ( ! config . url && ! config . baseURL ) {
230
+ throw new Error ( 'Invalid request configuration: missing URL or baseURL' )
188
231
}
189
232
190
233
return config
0 commit comments