@@ -643,25 +643,36 @@ export const createMcpServer = (): McpServerWrapper => {
643643 }
644644
645645 if ( name === ToolName . ZIP_RESOURCES ) {
646- const MAX_ZIP_FETCH_SIZE = Number ( process . env . MAX_ZIP_FETCH_SIZE ?? String ( 10 * 1024 * 1024 ) ) ; // 10 MB default
647- const MAX_ZIP_FETCH_TIME_MILLIS = Number ( process . env . MAX_ZIP_FETCH_TIME_MILLIS ?? String ( 30 * 1000 ) ) ; // 30 seconds default.
646+ const ZIP_MAX_FETCH_SIZE = Number ( process . env . ZIP_MAX_FETCH_SIZE ?? String ( 10 * 1024 * 1024 ) ) ; // 10 MB default
647+ const ZIP_MAX_FETCH_TIME_MILLIS = Number ( process . env . ZIP_MAX_FETCH_TIME_MILLIS ?? String ( 30 * 1000 ) ) ; // 30 seconds default.
648+ // Comma-separated list of allowed domains. Empty means all domains are allowed.
649+ const ZIP_ALLOWED_DOMAINS = ( process . env . ZIP_ALLOWED_DOMAINS ?? "raw.githubusercontent.com" ) . split ( "," ) . map ( d => d . trim ( ) . toLowerCase ( ) ) . filter ( d => d . length > 0 ) ;
648650
649651 const { files, outputType } = ZipResourcesInputSchema . parse ( args ) ;
650652 const zip = new JSZip ( ) ;
651653
652- let remainingUploadBytes = MAX_ZIP_FETCH_SIZE ;
653- const uploadEndTime = Date . now ( ) + MAX_ZIP_FETCH_TIME_MILLIS ;
654+ let remainingUploadBytes = ZIP_MAX_FETCH_SIZE ;
655+ const uploadEndTime = Date . now ( ) + ZIP_MAX_FETCH_TIME_MILLIS ;
654656
655657 for ( const [ fileName , urlString ] of Object . entries ( files ) ) {
656658 try {
657659 if ( remainingUploadBytes <= 0 ) {
658- throw new Error ( `Max upload size of ${ MAX_ZIP_FETCH_SIZE } bytes exceeded` ) ;
660+ throw new Error ( `Max upload size of ${ ZIP_MAX_FETCH_SIZE } bytes exceeded` ) ;
659661 }
660662
661663 const url = new URL ( urlString ) ;
662664 if ( url . protocol !== 'http:' && url . protocol !== 'https:' && url . protocol !== 'data:' ) {
663665 throw new Error ( `Unsupported URL protocol for ${ urlString } . Only http, https, and data URLs are supported.` ) ;
664666 }
667+ if ( ZIP_ALLOWED_DOMAINS . length > 0 && ( url . protocol === 'http:' || url . protocol === 'https:' ) ) {
668+ const domain = url . hostname ;
669+ const domainAllowed = ZIP_ALLOWED_DOMAINS . some ( allowedDomain => {
670+ return domain === allowedDomain || domain . endsWith ( `.${ allowedDomain } ` ) ;
671+ } ) ;
672+ if ( ! domainAllowed ) {
673+ throw new Error ( `Domain ${ domain } is not in the allowed domains list.` ) ;
674+ }
675+ }
665676
666677 const response = await fetchSafely ( url , {
667678 maxBytes : remainingUploadBytes ,
0 commit comments