@@ -140,7 +140,10 @@ const isValidURL = (url) => {
140140 } catch ( error ) {
141141 // If URL parsing fails, it might be a relative URL without protocol
142142 // 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
144147 }
145148}
146149
@@ -149,6 +152,7 @@ const isAllowedHost = (hostname) => {
149152 const allowedDomains = [
150153 'api.contentstack.io' ,
151154 'eu-api.contentstack.com' ,
155+ 'au-api.contentstack.com' ,
152156 'azure-na-api.contentstack.com' ,
153157 'azure-eu-api.contentstack.com' ,
154158 'gcp-na-api.contentstack.com' ,
@@ -177,14 +181,53 @@ const isAllowedHost = (hostname) => {
177181 } )
178182}
179183
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+
180214export 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 )
183226 }
184227
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' )
188231 }
189232
190233 return config
0 commit comments