You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2026/06/09 08:09:06 UTC

(poi) branch trunk updated: Harden duplicate ZIP entry validation against path separator bypasses (#1124)

This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 5b6a5f4710 Harden duplicate ZIP entry validation against path separator bypasses (#1124)
5b6a5f4710 is described below

commit 5b6a5f4710d873f8770b8e594350755e855e562a
Author: KALI 834X <ka...@gmail.com>
AuthorDate: Tue Jun 9 13:39:00 2026 +0530

    Harden duplicate ZIP entry validation against path separator bypasses (#1124)
    
    Co-authored-by: “sahvx655-wq” <“sahvx655@gmail.com”>
---
 .../util/ZipInputStreamZipEntrySource.java         |  2 +-
 .../apache/poi/openxml4j/util/ZipSecureFile.java   |  2 +-
 .../poi/openxml4j/util/TestZipSecureFile.java      | 29 ++++++++++++++++++++++
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
index ecdbb95ecf..37fa35e615 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
@@ -106,7 +106,7 @@ public class ZipInputStreamZipEntrySource implements ZipEntrySource {
             if (name == null || name.isEmpty()) {
                 throw new InvalidZipException("Input file contains an entry with an empty name");
             }
-            name = name.toLowerCase(Locale.ROOT);
+            name = name.replace('\\', '/').toLowerCase(Locale.ROOT);
             if (filenames.contains(name)) {
                 throw new InvalidZipException("Input file contains more than 1 entry with the name " + zipEntry.getName());
             }
diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipSecureFile.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
index 4ccb069f81..fbcc97ac2d 100644
--- a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
+++ b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
@@ -274,7 +274,7 @@ public class ZipSecureFile extends ZipFile {
             if (name == null || name.isEmpty()) {
                 throw new InvalidZipException("Input file contains an entry with an empty name");
             }
-            name = name.toLowerCase(Locale.ROOT);
+            name = name.replace('\\', '/').toLowerCase(Locale.ROOT);
             if (filenames.contains(name)) {
                 throw new InvalidZipException("Input file contains more than 1 entry with the name " + entry.getName());
             }
diff --git a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/util/TestZipSecureFile.java b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/util/TestZipSecureFile.java
index 261495f434..fb2885c0da 100644
--- a/poi-ooxml/src/test/java/org/apache/poi/openxml4j/util/TestZipSecureFile.java
+++ b/poi-ooxml/src/test/java/org/apache/poi/openxml4j/util/TestZipSecureFile.java
@@ -131,4 +131,33 @@ class TestZipSecureFile {
         // If the file descriptor was correctly closed, we should be able to delete the file
         assertTrue(tempFile.delete(), "Temporary file should be successfully deleted after constructor failure");
     }
+
+    @Test
+    void testValidateMixedSeparatorDuplicateEntryNames() throws Exception {
+        File tempFile = TempFile.createTempFile("mixed-duplicate-entries", ".zip");
+        try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(tempFile)) {
+            ZipArchiveEntry entry1 = new ZipArchiveEntry("sub/test.txt");
+            zos.putArchiveEntry(entry1);
+            zos.write("hello".getBytes(StandardCharsets.UTF_8));
+            zos.closeArchiveEntry();
+
+            ZipArchiveEntry entry2 = new ZipArchiveEntry("sub\\test.txt");
+            zos.putArchiveEntry(entry2);
+            zos.write("world".getBytes(StandardCharsets.UTF_8));
+            zos.closeArchiveEntry();
+        }
+
+        try {
+            // ZipSecureFile should detect the duplicate entries (mixed path separators)
+            assertThrows(IOException.class, () -> new ZipSecureFile(tempFile));
+
+            // ZipInputStreamZipEntrySource should also detect the duplicate entries
+            try (InputStream is = java.nio.file.Files.newInputStream(tempFile.toPath());
+                 ZipArchiveThresholdInputStream zis = org.apache.poi.openxml4j.opc.internal.ZipHelper.openZipStream(is)) {
+                assertThrows(IOException.class, () -> new ZipInputStreamZipEntrySource(zis));
+            }
+        } finally {
+            assertTrue(tempFile.delete(), "Temporary file should be successfully deleted");
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org