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

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git

commit 1ecd39bc5bda8b3b529aabd0a80384bed41a4984
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Aug 16 10:46:52 2026 -0400

    [GZip] GzipCompressorInputStream now throws CompressorException instead
    of IllegalArgumetException/IllegalStateException.
---
 src/changes/changes.xml                            |  2 +-
 .../compress/compressors/gzip/GzipParameters.java  | 17 ++++---
 .../gzip/GzipCompressorInputStreamTest.java        | 58 ++++++++++++++++++++++
 .../compressors/gzip/GzipParametersTest.java       |  9 ++--
 4 files changed, 73 insertions(+), 13 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3ab6c4fe2..f4e45f82a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -149,7 +149,7 @@ The <action> type attribute can be add,update,fix,remove.
       <!-- FIX gzip -->
       <action type="fix" dev="ggregory" due-to="Gary Gregory">[GZip] 
GzipParameters.setOperatingSystem(int) now throws CompressorException on 
illegal input.</action>
       <action type="fix" issue="COMPRESS-705" dev="ggregory" due-to="Mario 
Fredenhagen, Gary Gregory">[GZip] GZip IOException: Extra subfield length 
exceeds remaining bytes in extra field; use new option 
GzipCompressorInputStream.Builder.setIgnoreExtraField(boolean).</action>
-      <action type="fix" dev="ggregory" due-to="Gary Gregory">[GZip] 
GzipCompressorInputStream now throws CompressorException instead of 
IllegalArgumetException/IllegalStateException.</action>
+      <action type="fix" dev="ggregory" due-to="Gary Gregory, KALI 
834X">[GZip] GzipCompressorInputStream now throws CompressorException instead 
of IllegalArgumetException/IllegalStateException.</action>
       <!-- FIX snappy -->
       <action type="fix" dev="ggregory" due-to="Stanislav Fort, Gary 
Gregory">[Snappy] Fix for when a valid raw Snappy stream with uncompressed size 
> 2 GiB used to decompress and then fail at physical EOF with a “Premature end 
of stream” exception instead of completing cleanly.</action>
       <!-- FIX deflate64 -->
diff --git 
a/src/main/java/org/apache/commons/compress/compressors/gzip/GzipParameters.java
 
b/src/main/java/org/apache/commons/compress/compressors/gzip/GzipParameters.java
index d02ff11cf..eed5a87cf 100644
--- 
a/src/main/java/org/apache/commons/compress/compressors/gzip/GzipParameters.java
+++ 
b/src/main/java/org/apache/commons/compress/compressors/gzip/GzipParameters.java
@@ -26,6 +26,7 @@
 import java.util.zip.Deflater;
 
 import org.apache.commons.compress.CompressException;
+import org.apache.commons.compress.compressors.CompressorException;
 import org.apache.commons.io.Charsets;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -509,9 +510,9 @@ public int hashCode() {
                 operatingSystem, trailerCrc, trailerISize);
     }
 
-    private String requireNonNulByte(final String text) {
+    private String requireNonNulByte(final String text) throws 
CompressorException {
         if (StringUtils.isNotEmpty(text) && 
ArrayUtils.contains(text.getBytes(fileNameCharset), (byte) 0)) {
-            throw new IllegalArgumentException("String encoded in Charset '" + 
fileNameCharset + "' contains the nul byte 0 which is not supported in gzip.");
+            throw new CompressorException("String encoded in Charset '" + 
fileNameCharset + "' contains the nul byte 0 which is not supported in gzip.");
         }
         return text;
     }
@@ -534,9 +535,9 @@ public void setBufferSize(final int bufferSize) {
      * Sets an arbitrary user-defined comment.
      *
      * @param comment A user-defined comment.
-     * @throws IllegalArgumentException if the encoded bytes would contain a 
nul byte '\0' reserved for gzip field termination.
+     * @throws CompressorException if the encoded bytes would contain a nul 
byte '\0' reserved for gzip field termination.
      */
-    public void setComment(final String comment) {
+    public void setComment(final String comment) throws CompressorException {
         this.comment = requireNonNulByte(comment);
     }
 
@@ -586,11 +587,11 @@ void setExtraFieldXlen(final int extraFieldXlen) {
      * Sets the name of the compressed file.
      *
      * @param fileName The name of the file without the directory path.
-     * @throws IllegalArgumentException if the encoded bytes would contain a 
nul byte '\0' reserved for gzip field termination.
+     * @throws CompressorException if the encoded bytes would contain a nul 
byte '\0' reserved for gzip field termination.
      * @deprecated Use {@link #setFileName(String)}.
      */
     @Deprecated
-    public void setFilename(final String fileName) {
+    public void setFilename(final String fileName) throws CompressorException {
         setFileName(fileName);
     }
 
@@ -598,9 +599,9 @@ public void setFilename(final String fileName) {
      * Sets the name of the compressed file.
      *
      * @param fileName The name of the file without the directory path.
-     * @throws IllegalArgumentException if the encoded bytes would contain a 
nul byte '\0' reserved for gzip field termination.
+     * @throws CompressorException if the encoded bytes would contain a nul 
byte '\0' reserved for gzip field termination.
      */
-    public void setFileName(final String fileName) {
+    public void setFileName(final String fileName) throws CompressorException {
         this.fileName = requireNonNulByte(fileName);
     }
 
diff --git 
a/src/test/java/org/apache/commons/compress/compressors/gzip/GzipCompressorInputStreamTest.java
 
b/src/test/java/org/apache/commons/compress/compressors/gzip/GzipCompressorInputStreamTest.java
index 67301ec88..4b218a147 100644
--- 
a/src/test/java/org/apache/commons/compress/compressors/gzip/GzipCompressorInputStreamTest.java
+++ 
b/src/test/java/org/apache/commons/compress/compressors/gzip/GzipCompressorInputStreamTest.java
@@ -21,10 +21,14 @@
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -35,9 +39,13 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
 
 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.compressors.CompressorException;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.commons.io.function.IOStream;
@@ -353,4 +361,54 @@ void testReadGzipFileCreatedByCli() throws IOException {
         }
     }
 
+    private static byte[] gzipMemberWithFileName(final byte[] fileNameField) 
throws IOException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        bos.write(0x1f);
+        bos.write(0x8b); // magic
+        bos.write(0x08); // deflate
+        bos.write(0x08); // FLG: FNAME present
+        bos.write(new byte[] { 0, 0, 0, 0 }); // MTIME
+        bos.write(0x00); // XFL
+        bos.write(0xff); // OS unknown
+        bos.write(fileNameField);
+        bos.write(0x00); // NUL terminator for FNAME
+        final ByteArrayOutputStream deflated = new ByteArrayOutputStream();
+        try (DeflaterOutputStream dos = new DeflaterOutputStream(deflated, new 
Deflater(Deflater.DEFAULT_COMPRESSION, true))) {
+            // empty payload
+        }
+        bos.write(deflated.toByteArray());
+        final long crc = new CRC32().getValue();
+        bos.write((int) (crc & 0xff));
+        bos.write((int) (crc >> 8 & 0xff));
+        bos.write((int) (crc >> 16 & 0xff));
+        bos.write((int) (crc >> 24 & 0xff));
+        bos.write(new byte[] { 0, 0, 0, 0 }); // ISIZE
+        return bos.toByteArray();
+    }
+
+    /**
+     * A member whose FNAME field cannot be round-tripped through the 
configured charset must fail with a CompressorException rather than a raw
+     * IllegalArgumentException escaping the IOException contract of read().
+     */
+    @Test
+    void testUnencodableFileNameThrowsCompressorException() throws IOException 
{
+        // Under UTF-32BE a single stray byte decodes to U+FFFD, which 
re-encodes to bytes containing a NUL.
+        final byte[] gz = gzipMemberWithFileName(new byte[] { 0x41 });
+        assertThrows(CompressorException.class, () -> {
+            try (GzipCompressorInputStream gis = 
GzipCompressorInputStream.builder()
+                    .setInputStream(new ByteArrayInputStream(gz))
+                    .setFileNameCharset(Charset.forName("UTF-32BE"))
+                    .get()) {
+                IOUtils.toString(gis, StandardCharsets.ISO_8859_1);
+            }
+        });
+        // A member that round-trips cleanly still parses.
+        final byte[] ok = 
gzipMemberWithFileName("name.txt".getBytes(StandardCharsets.ISO_8859_1));
+        try (GzipCompressorInputStream gis = 
GzipCompressorInputStream.builder()
+                .setInputStream(new ByteArrayInputStream(ok))
+                .get()) {
+            assertEquals("", IOUtils.toString(gis, 
StandardCharsets.ISO_8859_1));
+            assertEquals("name.txt", gis.getMetaData().getFileName());
+        }
+    }
 }
diff --git 
a/src/test/java/org/apache/commons/compress/compressors/gzip/GzipParametersTest.java
 
b/src/test/java/org/apache/commons/compress/compressors/gzip/GzipParametersTest.java
index ecc5a466b..3d7194ed6 100644
--- 
a/src/test/java/org/apache/commons/compress/compressors/gzip/GzipParametersTest.java
+++ 
b/src/test/java/org/apache/commons/compress/compressors/gzip/GzipParametersTest.java
@@ -30,6 +30,7 @@
 import java.util.zip.Deflater;
 
 import org.apache.commons.compress.CompressException;
+import org.apache.commons.compress.compressors.CompressorException;
 import org.apache.commons.compress.compressors.gzip.GzipParameters.OS;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -91,11 +92,11 @@ void testIllegalCommentOrFileName(final Charset charset, 
final String text) {
         final GzipParameters gzipParameters = new GzipParameters();
         // null resets to default value
         gzipParameters.setFileNameCharset(charset);
-        assertThrows(IllegalArgumentException.class, () -> 
gzipParameters.setComment(text));
+        assertThrows(CompressorException.class, () -> 
gzipParameters.setComment(text));
         assertNull(gzipParameters.getComment());
-        assertThrows(IllegalArgumentException.class, () -> 
gzipParameters.setFilename(text));
+        assertThrows(CompressorException.class, () -> 
gzipParameters.setFilename(text));
         assertNull(gzipParameters.getFileName());
-        assertThrows(IllegalArgumentException.class, () -> 
gzipParameters.setFileName(text));
+        assertThrows(CompressorException.class, () -> 
gzipParameters.setFileName(text));
         assertNull(gzipParameters.getFileName());
         assertEquals(gzipParameters, gzipParameters);
     }
@@ -111,7 +112,7 @@ void testIllegalCommentOrFileName(final Charset charset, 
final String text) {
         "UTF-8     , helloéworld"
     })
     // @formatter:on
-    void testLegalCommentOrFileName(final Charset charset, final String text) {
+    void testLegalCommentOrFileName(final Charset charset, final String text) 
throws CompressorException {
         final GzipParameters gzipParameters = new GzipParameters();
         // null resets to default value
         gzipParameters.setFileNameCharset(charset);

Reply via email to