@@ -290,14 +290,31 @@ protected void extractFile(
290290            }
291291        }
292292
293-         // Hmm. Symlinks re-evaluate back to the original file here. Unsure if this is a good thing... 
294-         final  File  targetFileName  = FileUtils .resolveFile (dir , entryName );
293+         // For symlinks, we need to get the file path without following symlinks. 
294+         // FileUtils.resolveFile calls getCanonicalFile() which follows symlinks, 
295+         // causing the symlink to resolve to its target instead of the symlink itself. 
296+         final  File  targetFileName ;
297+         if  (!StringUtils .isEmpty (symlinkDestination )) {
298+             // For symlinks, use simple path resolution without canonicalization 
299+             targetFileName  = resolveFileWithoutFollowingSymlinks (dir , entryName );
300+         } else  {
301+             // For regular files and directories, use the existing logic 
302+             targetFileName  = FileUtils .resolveFile (dir , entryName );
303+         }
295304
296305        // Make sure that the resolved path of the extracted file doesn't escape the destination directory 
297306        // getCanonicalFile().toPath() is used instead of getCanonicalPath() (returns String), 
298307        // because "/opt/directory".startsWith("/opt/dir") would return false negative. 
299308        Path  canonicalDirPath  = dir .getCanonicalFile ().toPath ();
300-         Path  canonicalDestPath  = targetFileName .getCanonicalFile ().toPath ();
309+ 
310+         // For symlinks, we need to check the symlink path itself, not the target it points to 
311+         Path  canonicalDestPath ;
312+         if  (!StringUtils .isEmpty (symlinkDestination )) {
313+             // For symlinks, normalize without following the link 
314+             canonicalDestPath  = targetFileName .toPath ().toAbsolutePath ().normalize ();
315+         } else  {
316+             canonicalDestPath  = targetFileName .getCanonicalFile ().toPath ();
317+         }
301318
302319        if  (!canonicalDestPath .startsWith (canonicalDirPath )) {
303320            throw  new  ArchiverException ("Entry is outside of the target directory ("  + entryName  + ")" );
@@ -396,4 +413,33 @@ protected boolean shouldExtractEntry(File targetDirectory, File targetFileName,
396413    private  String  normalizedFileSeparator (String  pathOrEntry ) {
397414        return  pathOrEntry .replace ("/" , File .separator );
398415    }
416+ 
417+     /** 
418+      * Resolves a file path relative to a base directory without following symlinks. 
419+      * This is similar to FileUtils.resolveFile but doesn't call getCanonicalFile(), 
420+      * which would follow symlinks and resolve them to their targets. 
421+      * 
422+      * @param baseDir the base directory 
423+      * @param filename the filename to resolve 
424+      * @return the resolved file 
425+      */ 
426+     private  File  resolveFileWithoutFollowingSymlinks (File  baseDir , String  filename ) {
427+         String  filenm  = filename ;
428+         if  ('/'  != File .separatorChar ) {
429+             filenm  = filename .replace ('/' , File .separatorChar );
430+         }
431+ 
432+         if  ('\\'  != File .separatorChar ) {
433+             filenm  = filenm .replace ('\\' , File .separatorChar );
434+         }
435+ 
436+         // For absolute paths, just return a File object without canonicalization 
437+         if  (filenm .startsWith (File .separator )) {
438+             return  new  File (filenm );
439+         }
440+ 
441+         // For relative paths, combine with base directory and get absolute path 
442+         // but don't call getCanonicalFile() which would follow symlinks 
443+         return  new  File (baseDir , filenm ).getAbsoluteFile ();
444+     }
399445}
0 commit comments