@@ -114,27 +114,39 @@ async function fetchZipFromMediaLit(
114114}
115115
116116/**
117- * Validate ZIP entry name to prevent directory traversal
117+ * Sanitize ZIP entry name to prevent directory traversal.
118+ * Returns sanitized path or null if the entry is unsafe.
118119 */
119- function isSafeZipEntryName ( entryName : string ) : boolean {
120- if ( ! entryName ) return false ;
120+ function sanitizeZipEntryName ( entryName : string ) : string | null {
121+ if ( ! entryName ) return null ;
121122
122123 // ZIP specification uses '/' as directory separator; reject backslashes
123- if ( entryName . includes ( "\\" ) ) return false ;
124+ if ( entryName . includes ( "\\" ) ) return null ;
124125
125126 // Reject absolute paths or Windows drive-letter paths (e.g. "C:...").
126- if ( entryName . startsWith ( "/" ) ) return false ;
127- if ( / ^ [ a - z A - Z ] : / . test ( entryName ) ) return false ;
127+ if ( entryName . startsWith ( "/" ) ) return null ;
128+ if ( / ^ [ a - z A - Z ] : / . test ( entryName ) ) return null ;
128129
129- // Normalize and ensure there are no ".." segments.
130+ // Normalize path segments and filter out dangerous ones
130131 const segments = entryName . split ( "/" ) ;
132+ const safeSegments : string [ ] = [ ] ;
133+
131134 for ( const segment of segments ) {
135+ // Reject path traversal
132136 if ( segment === ".." ) {
133- return false ;
137+ return null ;
134138 }
139+ // Skip empty segments and current directory references
140+ if ( segment === "" || segment === "." ) {
141+ continue ;
142+ }
143+ safeSegments . push ( segment ) ;
135144 }
136145
137- return true ;
146+ if ( safeSegments . length === 0 ) return null ;
147+
148+ // Return a new sanitized path string
149+ return safeSegments . join ( path . sep ) ;
138150}
139151
140152/**
@@ -154,18 +166,20 @@ async function extractZipToDisk(
154166 const resolvedExtractDir = path . resolve ( extractDir ) ;
155167 for ( const entry of zip . getEntries ( ) ) {
156168 if ( ! entry . isDirectory ) {
157- // Validate entry name to prevent directory traversal (Zip Slip)
158- if ( ! isSafeZipEntryName ( entry . entryName ) ) {
169+ // Sanitize entry name to prevent directory traversal (Zip Slip)
170+ const sanitizedName = sanitizeZipEntryName ( entry . entryName ) ;
171+ if ( sanitizedName === null ) {
159172 error ( "Skipping unsafe ZIP entry name during extraction" , {
160173 entryName : entry . entryName ,
161174 } ) ;
162175 continue ;
163176 }
164177
165- const targetPath = path . join ( resolvedExtractDir , entry . entryName ) ;
178+ // Use sanitized name for path construction
179+ const targetPath = path . join ( resolvedExtractDir , sanitizedName ) ;
166180 const resolvedTargetPath = path . resolve ( targetPath ) ;
167181
168- // Prevent Zip Slip / directory traversal : ensure target stays within extractDir
182+ // Defense in depth : ensure target stays within extractDir
169183 if (
170184 resolvedTargetPath !== resolvedExtractDir &&
171185 ! resolvedTargetPath . startsWith ( resolvedExtractDir + path . sep )
0 commit comments