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 4644d25d67 Fix case-insensitive ZIP entry matching in 
ZipFileZipEntrySource under Turkish Locale (#1136)
4644d25d67 is described below

commit 4644d25d672a7092572e268fe96bceab92533fb9
Author: KALI 834X <[email protected]>
AuthorDate: Fri Jun 12 18:05:00 2026 +0530

    Fix case-insensitive ZIP entry matching in ZipFileZipEntrySource under 
Turkish Locale (#1136)
    
    * Fix temporary file leak in ZipArchiveFakeEntry constructor on copy failure
    
    * Fix case-insensitive ZIP entry matching in ZipFileZipEntrySource under 
Turkish Locale
    
    * Fix forbidden APIs error in Turkish Locale case-insensitive ZIP entry 
matching test
    
    * Optimize filename matching by removing lowercase conversion
    
    Remove unnecessary conversion of normalizedPath to lowercase.
    
    * need to run test in isolation
    
    ---------
    
    Co-authored-by: “sahvx655-wq” <“[email protected]”>
    Co-authored-by: PJ Fanning <[email protected]>
---
 .../poi/openxml4j/util/ZipArchiveFakeEntry.java    |  30 +++++--
 .../poi/openxml4j/util/TestZipSecureFile.java      | 100 ++++++++++++++++++++-
 2 files changed, 118 insertions(+), 12 deletions(-)

diff --git 
a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java
 
b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java
index cb2cd2996a..f617a8dde9 100644
--- 
a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java
+++ 
b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java
@@ -72,16 +72,28 @@ public final class ZipArchiveFakeEntry extends 
ZipArchiveEntry implements Closea
 
         final int threshold = 
ZipInputStreamZipEntrySource.getThresholdBytesForTempFiles();
         if (threshold >= 0 && (entrySize >= threshold || entrySize == -1)) {
-            if (ZipInputStreamZipEntrySource.shouldEncryptTempFiles()) {
-                encryptedTempData = new EncryptedTempData();
-                try (OutputStream os = encryptedTempData.getOutputStream()) {
-                    IOUtils.copy(inp, os);
+            boolean success = false;
+            try {
+                if (ZipInputStreamZipEntrySource.shouldEncryptTempFiles()) {
+                    encryptedTempData = new EncryptedTempData();
+                    try (OutputStream os = 
encryptedTempData.getOutputStream()) {
+                        IOUtils.copy(inp, os);
+                    }
+                } else {
+                    tempFile = TempFile.createTempFile("poi-zip-entry", 
".tmp");
+                    LOG.atInfo().log("Creating temp file {} for zip entry {} 
of size {} bytes",
+                            tempFile.getAbsolutePath(), entry.getName(), 
entrySize);
+                    IOUtils.copy(inp, tempFile);
+                }
+                success = true;
+            } finally {
+                if (!success) {
+                    try {
+                        close();
+                    } catch (IOException e) {
+                        LOG.atWarn().withThrowable(e).log("Failed to clean up 
temporary resources on construction failure");
+                    }
                 }
-            } else {
-                tempFile = TempFile.createTempFile("poi-zip-entry", ".tmp");
-                LOG.atInfo().log("Creating temp file {} for zip entry {} of 
size {} bytes",
-                        tempFile.getAbsolutePath(), entry.getName(), 
entrySize);
-                IOUtils.copy(inp, tempFile);
             }
         } else {
             if (entrySize < -1 || entrySize >= Integer.MAX_VALUE) {
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 8771ebf38c..8ea2fd8b28 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
@@ -26,9 +26,10 @@ import org.apache.poi.util.TempFile;
 import org.apache.poi.util.TempFileCreationStrategy;
 import org.apache.poi.xssf.XSSFTestDataSamples;
 import org.junit.jupiter.api.Test;
+import org.apache.poi.util.SuppressForbidden;
+import org.junit.jupiter.api.parallel.Isolated;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
@@ -36,11 +37,11 @@ import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
+import java.util.Locale;
 
 import static org.junit.jupiter.api.Assertions.*;
 
+@Isolated // modifies the default locale and we don't want to affect tests 
running in parallel
 class TestZipSecureFile {
     @Test
     void testThresholdInputStream() throws Exception {
@@ -224,4 +225,97 @@ class TestZipSecureFile {
             assertTrue(tempFile.delete(), "Temporary file should be 
successfully deleted");
         }
     }
+
+    @Test
+    void testZipInputStreamZipEntrySourceCopyFailureReleasesResources() throws 
Exception {
+        List<File> createdFiles = new ArrayList<>();
+        TempFileCreationStrategy customStrategy = new 
TempFileCreationStrategy() {
+            private final TempFileCreationStrategy delegate = new 
DefaultTempFileCreationStrategy();
+            @Override
+            public File createTempFile(String prefix, String suffix) throws 
IOException {
+                File f = delegate.createTempFile(prefix, suffix);
+                createdFiles.add(f);
+                return f;
+            }
+            @Override
+            public File createTempDirectory(String prefix) throws IOException {
+                return delegate.createTempDirectory(prefix);
+            }
+        };
+
+        TempFile.withStrategy(customStrategy, () -> {
+            try {
+                int oldThreshold = 
ZipInputStreamZipEntrySource.getThresholdBytesForTempFiles();
+                ZipInputStreamZipEntrySource.setThresholdBytesForTempFiles(0);
+                try {
+                    File tempZipFile = 
TempFile.createTempFile("test-leak-copy", ".zip");
+                    try {
+                        try (ZipArchiveOutputStream zos = new 
ZipArchiveOutputStream(tempZipFile)) {
+                            ZipArchiveEntry entry = new 
ZipArchiveEntry("test.txt");
+                            zos.putArchiveEntry(entry);
+                            zos.write("some data that will fail during 
read".getBytes(StandardCharsets.UTF_8));
+                            zos.closeArchiveEntry();
+                        }
+
+                        try (InputStream is = 
Files.newInputStream(tempZipFile.toPath());
+                             ZipArchiveThresholdInputStream zis = new 
ZipArchiveThresholdInputStream(
+                                     new 
org.apache.commons.compress.archivers.zip.ZipArchiveInputStream(is)) {
+                                 private int readCount = 0;
+                                 @Override
+                                 public int read(byte[] b, int off, int len) 
throws IOException {
+                                     readCount += len;
+                                     if (readCount > 5) {
+                                         throw new IOException("Simulated copy 
failure");
+                                     }
+                                     return super.read(b, off, len);
+                                 }
+                             }) {
+                            assertThrows(IOException.class, () -> new 
ZipInputStreamZipEntrySource(zis));
+                        }
+                    } finally {
+                        tempZipFile.delete();
+                    }
+                } finally {
+                    
ZipInputStreamZipEntrySource.setThresholdBytesForTempFiles(oldThreshold);
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            return null;
+        });
+
+        assertFalse(createdFiles.isEmpty(), "At least one temporary file 
should have been created for the zip entry");
+        for (File f : createdFiles) {
+            assertFalse(f.exists(), "Temporary file " + f.getAbsolutePath() + 
" should have been deleted on copy failure");
+        }
+    }
+
+    @Test
+    @SuppressForbidden("test code")
+    void testZipFileZipEntrySourceCaseInsensitiveMatchingUnderTurkishLocale() 
throws Exception {
+        Locale defaultLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("tr", "TR"));
+
+            File tempFile = TempFile.createTempFile("turkish-test", ".zip");
+            try {
+                try (ZipArchiveOutputStream zos = new 
ZipArchiveOutputStream(tempFile)) {
+                    ZipArchiveEntry entry = new ZipArchiveEntry("content.xml");
+                    zos.putArchiveEntry(entry);
+                    zos.write("data".getBytes(StandardCharsets.UTF_8));
+                    zos.closeArchiveEntry();
+                }
+
+                try (ZipSecureFile zipFile = new ZipSecureFile(tempFile)) {
+                    ZipFileZipEntrySource source = new 
ZipFileZipEntrySource(zipFile);
+                    ZipArchiveEntry entry = source.getEntry("Content.xml");
+                    assertNotNull(entry, "Should find the entry 
case-insensitively even under Turkish locale");
+                }
+            } finally {
+                tempFile.delete();
+            }
+        } finally {
+            Locale.setDefault(defaultLocale);
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to