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]