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 <[email protected]>
AuthorDate: Tue Jun 9 13:39:00 2026 +0530
Harden duplicate ZIP entry validation against path separator bypasses
(#1124)
Co-authored-by: “sahvx655-wq” <“[email protected]”>
---
.../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: [email protected]
For additional commands, e-mail: [email protected]