martin-g commented on code in PR #1432:
URL: https://github.com/apache/wicket/pull/1432#discussion_r3122879719


##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java:
##########
@@ -60,14 +61,42 @@ public File getFolder() {
         return folder;
     }
 
+    /**
+     * Returns the path for a directory for use in path traversal checks. Uses 
{@code toRealPath()}
+     * when the directory exists; otherwise falls back to {@code 
toAbsolutePath().normalize()} to
+     * support validation when the directory has not yet been created.
+     */
+    private static Path getPathForComparison(java.io.File dir) throws 
IOException
+    {
+        try
+        {
+            return dir.toPath().toRealPath();
+        }
+        catch (IOException e)
+        {
+            return dir.toPath().toAbsolutePath().normalize();
+        }
+    }
+
     @Override
     public void save(FileUpload fileItem, String uploadFieldId)
     {
-        File uploadFieldFolder = new File(getFolder(), uploadFieldId);
-        uploadFieldFolder.mkdirs();
         try
         {
-            IOUtils.copy(fileItem.getInputStream(),  new FileOutputStream(new 
File(uploadFieldFolder, fileItem.getClientFileName())));
+            Path baseFolderPath = getFolder().toPath().toRealPath();
+            java.io.File uploadFieldFolder = new File(getFolder(), 
uploadFieldId).getCanonicalFile();
+            if (!uploadFieldFolder.toPath().startsWith(baseFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in 
uploadFieldId");
+            }
+            uploadFieldFolder.mkdirs();
+            java.io.File target = new File(uploadFieldFolder, 
fileItem.getClientFileName()).getCanonicalFile();
+            Path uploadFieldFolderPath = 
getPathForComparison(uploadFieldFolder);
+            if (!target.toPath().startsWith(uploadFieldFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in client 
filename");
+            }
+            IOUtils.copy(fileItem.getInputStream(), new 
FileOutputStream(target));

Review Comment:
   This does not close the streams.
   ```suggestion
               try (java.io.InputStream in = fileItem.getInputStream();
                    java.io.FileOutputStream out = new FileOutputStream(target))
               {
                   IOUtils.copy(in, out);
               }
   ```
   



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java:
##########
@@ -78,6 +107,25 @@ public void save(FileUpload fileItem, String uploadFieldId)
     @Override
     public File getFile(String uploadFieldId, String clientFileName)
     {
-        return new File(new File(getFolder(), uploadFieldId), clientFileName);
+        try
+        {
+            Path baseFolderPath = getFolder().toPath().toRealPath();
+            java.io.File uploadFieldFolder = new File(getFolder(), 
uploadFieldId).getCanonicalFile();
+            if (!uploadFieldFolder.toPath().startsWith(baseFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in 
uploadFieldId");
+            }
+            java.io.File target = new File(uploadFieldFolder, 
clientFileName).getCanonicalFile();
+            Path uploadFieldFolderPath = 
getPathForComparison(uploadFieldFolder);
+            if (!target.toPath().startsWith(uploadFieldFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in client 
filename");
+            }

Review Comment:
   Let's extract the duplicated code and reuse it



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java:
##########
@@ -60,14 +61,42 @@ public File getFolder() {
         return folder;
     }
 
+    /**
+     * Returns the path for a directory for use in path traversal checks. Uses 
{@code toRealPath()}
+     * when the directory exists; otherwise falls back to {@code 
toAbsolutePath().normalize()} to
+     * support validation when the directory has not yet been created.
+     */
+    private static Path getPathForComparison(java.io.File dir) throws 
IOException
+    {
+        try
+        {
+            return dir.toPath().toRealPath();
+        }
+        catch (IOException e)
+        {
+            return dir.toPath().toAbsolutePath().normalize();
+        }
+    }
+
     @Override
     public void save(FileUpload fileItem, String uploadFieldId)
     {
-        File uploadFieldFolder = new File(getFolder(), uploadFieldId);
-        uploadFieldFolder.mkdirs();
         try
         {
-            IOUtils.copy(fileItem.getInputStream(),  new FileOutputStream(new 
File(uploadFieldFolder, fileItem.getClientFileName())));
+            Path baseFolderPath = getFolder().toPath().toRealPath();
+            java.io.File uploadFieldFolder = new File(getFolder(), 
uploadFieldId).getCanonicalFile();
+            if (!uploadFieldFolder.toPath().startsWith(baseFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in 
uploadFieldId");
+            }
+            uploadFieldFolder.mkdirs();

Review Comment:
   The result of `mkdirs()` is ignored. 



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java:
##########
@@ -78,6 +107,25 @@ public void save(FileUpload fileItem, String uploadFieldId)
     @Override
     public File getFile(String uploadFieldId, String clientFileName)
     {
-        return new File(new File(getFolder(), uploadFieldId), clientFileName);
+        try
+        {
+            Path baseFolderPath = getFolder().toPath().toRealPath();
+            java.io.File uploadFieldFolder = new File(getFolder(), 
uploadFieldId).getCanonicalFile();
+            if (!uploadFieldFolder.toPath().startsWith(baseFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in 
uploadFieldId");
+            }
+            java.io.File target = new File(uploadFieldFolder, 
clientFileName).getCanonicalFile();
+            Path uploadFieldFolderPath = 
getPathForComparison(uploadFieldFolder);
+            if (!target.toPath().startsWith(uploadFieldFolderPath))
+            {
+                throw new SecurityException("Path traversal detected in client 
filename");
+            }
+            return new File(target);
+        }
+        catch (IOException e)
+        {
+            throw new WicketRuntimeException(e);
+        }
     }

Review Comment:
   Unit tests could be added:
   * uploadFieldId = "../escaped" — must throw SecurityException
   * clientFileName = "../../etc/passwd" — must throw SecurityException
   * Normal valid uploadFieldId and clientFileName — must succeed



##########
wicket-core/src/main/java/org/apache/wicket/markup/html/form/upload/resource/FolderUploadsFileManager.java:
##########
@@ -60,14 +61,42 @@ public File getFolder() {
         return folder;
     }
 
+    /**
+     * Returns the path for a directory for use in path traversal checks. Uses 
{@code toRealPath()}
+     * when the directory exists; otherwise falls back to {@code 
toAbsolutePath().normalize()} to
+     * support validation when the directory has not yet been created.
+     */
+    private static Path getPathForComparison(java.io.File dir) throws 
IOException
+    {
+        try
+        {
+            return dir.toPath().toRealPath();
+        }
+        catch (IOException e)

Review Comment:
   Why IOExceptions are ignored here ?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to