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

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


The following commit(s) were added to refs/heads/master by this push:
     new e72c032d6 [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected 
record signature: 0x0" (#753)
e72c032d6 is described below

commit e72c032d6a1e142d0b0d9dde9b61d473f170755b
Author: Gary Gregory <[email protected]>
AuthorDate: Tue Jan 13 08:59:27 2026 -0500

    [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected record 
signature: 0x0" (#753)
    
    * [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected record
    signature: 0x0"
    
    * [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected record
    signature: 0x0"
    
    * Checkstyle
    
    * [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected record
    signature: 0x0"
    
    Refactor test
    
    * [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected record
    signature: 0x0"
    
    ZipCompress715Test now asserts checksum values
    
    * [COMPRESS-715] ZipArchiveInputStream fails with "Unexpected record
    signature: 0x0"
    
    Apply Piotr's suggestion: I couldn't find any documentation that
    describes the process of padding a ZIP file. Therefore, we must assume
    that the “padding” is just arbitrary data.
---
 .gitignore                                         |   1 +
 src/conf/checkstyle/checkstyle.xml                 |   2 +-
 .../archivers/zip/ZipArchiveInputStream.java       |  59 +-----------
 .../commons/compress/archivers/zip/ZipLong.java    |   2 +
 .../compress/archivers/zip/ZipCompress715Test.java | 103 +++++++++++++++++++++
 .../commons/compress/COMPRESS-715/compress715.zip  | Bin 0 -> 1287745 bytes
 6 files changed, 110 insertions(+), 57 deletions(-)

diff --git a/.gitignore b/.gitignore
index 92a0cd4ef..560a200e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ target
 /.externalToolBuilders/
 /maven-eclipse.xml
 /.java-version
+/.checkstyle
 
 # NetBeans files
 nb-configuration.xml
diff --git a/src/conf/checkstyle/checkstyle.xml 
b/src/conf/checkstyle/checkstyle.xml
index 83dc97197..7e8923a44 100644
--- a/src/conf/checkstyle/checkstyle.xml
+++ b/src/conf/checkstyle/checkstyle.xml
@@ -26,7 +26,7 @@ limitations under the License.
   </module>
   <module name="JavadocPackage" />
   <module name="LineLength">
-    <property name="max" value="160" />
+    <property name="max" value="170" />
   </module>
   <module name="NewlineAtEndOfFile">
     <!-- Files are saved in the repository with LF line endings; on Windows, 
use git config core.autocrlf input -->
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
 
b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
index bceaed7db..8cc75880c 100644
--- 
a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
+++ 
b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java
@@ -30,10 +30,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PushbackInputStream;
-import java.math.BigInteger;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
 import java.util.function.Function;
 import java.util.zip.CRC32;
 import java.util.zip.DataFormatException;
@@ -333,8 +331,6 @@ private <T extends InputStream> T checkInputStream() throws 
ZipException {
     private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();
     private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();
     private static final byte[] DD = ZipLong.DD_SIG.getBytes();
-    private static final byte[] APK_SIGNING_BLOCK_MAGIC = { 'A', 'P', 'K', ' 
', 'S', 'i', 'g', ' ', 'B', 'l', 'o', 'c', 'k', ' ', '4', '2', };
-    private static final BigInteger LONG_MAX = 
BigInteger.valueOf(Long.MAX_VALUE);
 
     /**
      * Creates a new builder.
@@ -856,12 +852,9 @@ public ZipArchiveEntry getNextZipEntry() throws 
IOException {
         }
         final ZipLong sig = new ZipLong(lfhBuf);
         if (!sig.equals(ZipLong.LFH_SIG)) {
-            if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG) || 
isApkSigningBlock(lfhBuf)) {
-                hitCentralDirectory = true;
-                skipRemainderOfArchive();
-                return null;
-            }
-            throw new ZipException(String.format("Unexpected record signature: 
0x%x", sig.getValue()));
+            hitCentralDirectory = true;
+            skipRemainderOfArchive();
+            return null;
         }
         // off: go past the signature
         int off = WORD;
@@ -964,52 +957,6 @@ public long getUncompressedCount() {
         return uncompressedCount;
     }
 
-    /**
-     * Checks whether this might be an APK Signing Block.
-     * <p>
-     * Unfortunately the APK signing block does not start with some kind of 
signature, it rather ends with one. It starts with a length, so what we do is 
parse
-     * the suspect length, skip ahead far enough, look for the signature and 
if we've found it, return true.
-     * </p>
-     *
-     * @param suspectLocalFileHeader the bytes read from the underlying stream 
in the expectation that they would hold the local file header of the next entry.
-     * @return true if this looks like an APK signing block.
-     * @see <a 
href="https://source.android.com/security/apksigning/v2";>https://source.android.com/security/apksigning/v2</a>
-     */
-    private boolean isApkSigningBlock(final byte[] suspectLocalFileHeader) 
throws IOException {
-        // length of block excluding the size field itself
-        final BigInteger len = 
ZipEightByteInteger.getValue(suspectLocalFileHeader);
-        // LFH has already been read and all but the first eight bytes contain 
(part of) the APK signing block,
-        // also subtract 16 bytes in order to position us at the magic string
-        BigInteger toSkip = len.add(BigInteger.valueOf(DWORD - 
suspectLocalFileHeader.length - (long) APK_SIGNING_BLOCK_MAGIC.length));
-        final byte[] magic = new byte[APK_SIGNING_BLOCK_MAGIC.length];
-        try {
-            if (toSkip.signum() < 0) {
-                // suspectLocalFileHeader contains the start of suspect magic 
string
-                final int off = suspectLocalFileHeader.length + 
toSkip.intValue();
-                // length was shorter than magic length
-                if (off < DWORD) {
-                    return false;
-                }
-                final int bytesInBuffer = Math.abs(toSkip.intValue());
-                System.arraycopy(suspectLocalFileHeader, off, magic, 0, 
Math.min(bytesInBuffer, magic.length));
-                if (bytesInBuffer < magic.length) {
-                    readFully(magic, bytesInBuffer);
-                }
-            } else {
-                while (toSkip.compareTo(LONG_MAX) > 0) {
-                    realSkip(Long.MAX_VALUE);
-                    toSkip = toSkip.add(LONG_MAX.negate());
-                }
-                realSkip(toSkip.longValue());
-                readFully(magic);
-            }
-        } catch (final EOFException ex) { // NOSONAR
-            // length was invalid
-            return false;
-        }
-        return Arrays.equals(magic, APK_SIGNING_BLOCK_MAGIC);
-    }
-
     private boolean isFirstByteOfEocdSig(final int b) {
         return b == ZipArchiveOutputStream.EOCD_SIG[0];
     }
diff --git 
a/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java 
b/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
index 01b43391a..398b4bc4c 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipLong.java
@@ -50,6 +50,8 @@ public final class ZipLong implements Cloneable, Serializable 
{
      */
     public static final ZipLong DD_SIG = new ZipLong(0X08074B50L);
 
+    static final ZipLong ZERO = new ZipLong(0);
+
     /**
      * Value stored in size and similar fields if ZIP64 extensions are used.
      *
diff --git 
a/src/test/java/org/apache/commons/compress/archivers/zip/ZipCompress715Test.java
 
b/src/test/java/org/apache/commons/compress/archivers/zip/ZipCompress715Test.java
new file mode 100644
index 000000000..2439f9ba1
--- /dev/null
+++ 
b/src/test/java/org/apache/commons/compress/archivers/zip/ZipCompress715Test.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.commons.compress.archivers.zip;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.IOException;
+import java.util.zip.CRC32;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.function.IOConsumer;
+import org.apache.commons.io.input.ChecksumInputStream;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests https://issues.apache.org/jira/browse/COMPRESS-715
+ */
+class ZipCompress715Test {
+
+    private void assertNextEntry(final ZipArchiveInputStream inputStream, 
final String expectedName, final int expectedSize, final String 
expectedCrcRadix16)
+            throws IOException {
+        final ZipArchiveEntry nextEntry = inputStream.getNextEntry();
+        assertEquals(expectedName, nextEntry.getName());
+        assertEquals(expectedSize, nextEntry.getSize());
+        final long crc = nextEntry.getCrc();
+        final long expectedCrc = Long.parseUnsignedLong(expectedCrcRadix16, 
16);
+        assertEquals(expectedCrc, crc, () -> String.format("0x%x", crc));
+        // @formatter:off
+        @SuppressWarnings("resource")
+        final ChecksumInputStream checksum = ChecksumInputStream.builder()
+                .setInputStream(inputStream)
+                .setChecksum(new CRC32())
+                .setExpectedChecksumValue(expectedCrc)
+                .setCountThreshold(expectedSize)
+                .get();
+        // @formatter:on
+        IOUtils.consume(checksum);
+    }
+
+    /**
+     * The test fixture unzips OK using {@code UnZip 6.00 of 20 April 2009, by 
Info-ZIP, with modifications by Apple Inc.}
+     *
+     * <p>
+     * Running:
+     * </p>
+     * <pre>
+     * unzip -lv compress715.zip
+     * </pre>
+     * <p>
+     * returns:
+     * </p>
+     *
+     * <pre>
+     * Archive:  compress715.zip
+     *  Length   Method    Size  Cmpr    Date    Time   CRC-32   Name
+     * --------  ------  ------- ---- ---------- ----- --------  ----
+     *   341552  Defl:N    14034  96% 03-18-2024 21:36 88cbd694  
50700006_82901717_20240318�?162450_Speichergruppe-14_Monitoring_AMT_M5190M_12394_986197575850.mf4
+     *   641560  Defl:N    49549  92% 03-18-2024 21:36 ef0dba37  
50700006_82901717_20240318�?162450_Speichergruppe-11_Monitoring_CAN_M5190M_7651_986197575850.mf4
+     *    37280  Defl:X    36887   1% 03-18-2024 21:36 ba89ba80  
50700006_82901717_20240318�?162450_MEA_5190.dmp
+     *   405692  Defl:X   371118   9% 03-18-2024 21:24 1e1402e5  
50700006_82901717_20240318�?162450_CFG_5190.ilf
+     *   847104  Defl:X   812041   4% 03-18-2024 21:24 d0b8c08d  
50700006_82901717_20240318�?162450_CFG_5190.zip
+     *    11107  Defl:X     2764  75% 03-18-2024 21:36 bb2f0f57  
50700006_82901717_20240318�?162450_MEA_5190.LOG
+     * --------          -------  ---                            -------
+     *  2284295          1286393  44%                            6 files
+     * </pre>
+     *
+     * @throws IOException Thrown if the test fails.
+     */
+    @Test
+    void testCompress715() throws IOException {
+        final String fixture = 
"src/test/resources/org/apache/commons/compress/COMPRESS-715/compress715.zip";
+        try (ZipArchiveInputStream inputStream = 
ZipArchiveInputStream.builder().setPath(fixture).get()) {
+            inputStream.forEach(IOConsumer.noop());
+        }
+        try (ZipArchiveInputStream inputStream = 
ZipArchiveInputStream.builder().setPath(fixture).get()) {
+            assertNextEntry(inputStream, 
"50700006_82901717_20240318–162450_Speichergruppe-14_Monitoring_AMT_M5190M_12394_986197575850.mf4",
 341_552, "88cbd694");
+            assertNextEntry(inputStream, 
"50700006_82901717_20240318–162450_Speichergruppe-11_Monitoring_CAN_M5190M_7651_986197575850.mf4",
 641_560, "ef0dba37");
+            assertNextEntry(inputStream, 
"50700006_82901717_20240318–162450_MEA_5190.dmp", 37_280, "ba89ba80");
+            assertNextEntry(inputStream, 
"50700006_82901717_20240318–162450_CFG_5190.ilf", 405_692, "1e1402e5");
+            assertNextEntry(inputStream, 
"50700006_82901717_20240318–162450_CFG_5190.zip", 847_104, "d0b8c08d");
+            assertNextEntry(inputStream, 
"50700006_82901717_20240318–162450_MEA_5190.LOG", 11_107, "bb2f0f57");
+            assertNull(inputStream.getNextEntry());
+        }
+    }
+}
diff --git 
a/src/test/resources/org/apache/commons/compress/COMPRESS-715/compress715.zip 
b/src/test/resources/org/apache/commons/compress/COMPRESS-715/compress715.zip
new file mode 100644
index 000000000..ef55fe455
Binary files /dev/null and 
b/src/test/resources/org/apache/commons/compress/COMPRESS-715/compress715.zip 
differ

Reply via email to