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 0c67cb8e5 [COMPRESS-700] Can't detect file flutter_awesome_buttons-0.1.0.tar as a TAR file 0c67cb8e5 is described below commit 0c67cb8e5a1221acbd427395036d408b53dc9f99 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon Jun 2 08:47:40 2025 -0400 [COMPRESS-700] Can't detect file flutter_awesome_buttons-0.1.0.tar as a TAR file --- src/changes/changes.xml | 3 +- .../compress/archivers/ArchiveStreamFactory.java | 9 ++- .../commons/compress/DetectArchiverTest.java | 73 +++++++++++++++++++-- .../compress/archivers/tar/Compress700Test.java | 10 ++- ...-fail.tar => flutter_awesome_buttons-0.1.0.tar} | Bin 5 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 27b152a5c..b06034e5e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -96,7 +96,8 @@ The <action> type attribute can be add,update,fix,remove. <action type="fix" dev="ggregory" due-to="Gary Gregory">ArArchiveInputStream.getBSDLongName(String) now throws its EOFException with a message.</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">ZipEncodingHelper.getZipEncoding(*) can throw NullPointerException and IllegalArgumentException on bad input instead of returning a value using the default Charset.</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">Javadoc improvements throughout.</action> - <action type="fix" dev="ggregory" due-to="Gary Gregory" issue= "COMPRESS-699">ArchiveStreamFactory.detect(inputStream) ArchiveException for TAR regression.</action> + <action type="fix" dev="ggregory" due-to="Gary Gregory" issue="COMPRESS-699">ArchiveStreamFactory.detect(inputStream) ArchiveException for TAR regression.</action> + <action type="fix" dev="ggregory" due-to="Gary Gregory" issue="COMPRESS-700">Can't detect file flutter_awesome_buttons-0.1.0.tar as a TAR file.</action> <!-- ADD --> <action type="add" dev="ggregory" due-to="Gary Gregory">Add GzipParameters.getModificationInstant().</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add GzipParameters.setModificationInstant(Instant).</action> diff --git a/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java b/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java index 4e066e4e7..88eb793a7 100644 --- a/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java +++ b/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java @@ -272,14 +272,14 @@ public static String detect(final InputStream in) throws ArchiveException { if (signatureLength >= TAR_HEADER_SIZE) { try (TarArchiveInputStream inputStream = new TarArchiveInputStream(new ByteArrayInputStream(tarHeader))) { // COMPRESS-191 - verify the header checksum - // COMPRESS-644 - do not allow zero byte file entries TarArchiveEntry entry = inputStream.getNextEntry(); // try to find the first non-directory entry within the first 10 entries. int count = 0; while (entry != null && entry.isDirectory() && entry.isCheckSumOK() && count++ < TAR_TEST_ENTRY_COUNT) { entry = inputStream.getNextEntry(); } - if (entry != null && entry.isCheckSumOK() && !entry.isDirectory() && entry.getSize() > 0 || count > 0) { + if (entry != null && entry.isCheckSumOK() && !entry.isDirectory() && isName(entry.getGroupName()) && isName(entry.getName()) + && isName(entry.getUserName()) || count > 0) { return TAR; } } catch (final Exception ignored) { @@ -347,6 +347,11 @@ public static SortedMap<String, ArchiveStreamProvider> findAvailableArchiveOutpu }); } + private static boolean isName(final String value) { + // Expect ASCII https://www.mkssoftware.com/docs/man4/tar.4.asp + return value.isEmpty() || value.chars().allMatch(ch -> ch > 31 && ch < 128); + } + static void putAll(final Set<String> names, final ArchiveStreamProvider provider, final TreeMap<String, ArchiveStreamProvider> map) { names.forEach(name -> map.put(toKey(name), provider)); } diff --git a/src/test/java/org/apache/commons/compress/DetectArchiverTest.java b/src/test/java/org/apache/commons/compress/DetectArchiverTest.java index f6c96a000..aafd12d3c 100644 --- a/src/test/java/org/apache/commons/compress/DetectArchiverTest.java +++ b/src/test/java/org/apache/commons/compress/DetectArchiverTest.java @@ -18,18 +18,23 @@ */ package org.apache.commons.compress; -import static org.junit.Assert.assertNull; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.attribute.FileTime; +import java.time.Instant; +import java.util.Collections; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveException; @@ -38,6 +43,7 @@ import org.apache.commons.compress.archivers.ar.ArArchiveInputStream; import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream; import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.junit.jupiter.api.Test; @@ -75,6 +81,10 @@ private BufferedInputStream createBufferedInputStream(final String resource) thr return new BufferedInputStream(newInputStream(resource)); } + private boolean isValidName(final String value) { + return value.isEmpty() || value.chars().allMatch(ch -> ch > 31 && ch < 128); + } + @Test public void testCOMPRESS_117() throws Exception { try (ArchiveInputStream<?> tar = createArchiveInputStream("COMPRESS-117.tar")) { @@ -131,11 +141,6 @@ public void testDetection() throws Exception { } - @Test - public void testDetectionNotArchive() { - assertThrows(ArchiveException.class, () -> createArchiveInputStream("test.txt")); - } - // Check that the empty archives created by the code are readable // Not possible to detect empty "ar" archive as it is completely empty @@ -143,6 +148,11 @@ public void testDetectionNotArchive() { // emptyArchive("ar"); // } + @Test + public void testDetectionNotArchive() { + assertThrows(ArchiveException.class, () -> createArchiveInputStream("test.txt")); + } + @Test public void testDetectOldTarFormatArchive() throws Exception { try (ArchiveInputStream<?> tar = createArchiveInputStream("COMPRESS-612/test-times-star-folder.tar")) { @@ -184,4 +194,55 @@ public void testIcoFile() { } }); } + + @Test + public void testIcoFileFirstTarArchiveEntry() throws Exception { + try (TarArchiveInputStream inputStream = new TarArchiveInputStream(createBufferedInputStream("org/apache/commons/compress/COMPRESS-644/ARW05UP.ICO"))) { + final TarArchiveEntry entry = inputStream.getNextEntry(); + // Find hints that the file is not a TAR file. + assertNull(entry.getCreationTime()); + assertEquals(-1, entry.getDataOffset()); + assertEquals(0, entry.getDevMajor()); + assertEquals(0, entry.getDevMinor()); + assertEquals(Collections.emptyMap(), entry.getExtraPaxHeaders()); + assertEquals(0, entry.getGroupId()); + assertFalse(isValidName(entry.getGroupName()), entry::getGroupName); // hint + assertNull(entry.getLastAccessTime()); + assertEquals(0, entry.getLastModifiedDate().getTime()); // hint + assertEquals(FileTime.from(Instant.EPOCH), entry.getLastModifiedTime()); // hint + assertEquals(0, entry.getLinkFlag()); // NUL (not really a hint) + assertEquals("", entry.getLinkName()); + assertEquals(0, entry.getLongGroupId()); + assertEquals(16777215, entry.getLongUserId()); // hint? + assertEquals(0, entry.getMode()); // hint? + assertEquals(0, entry.getModTime().getTime()); // hint? + assertFalse(isValidName(entry.getName()), entry::getName); // hint + assertNull(entry.getPath()); + assertEquals(0, entry.getRealSize()); + assertEquals(0, entry.getSize()); + assertNull(entry.getStatusChangeTime()); + assertEquals(16777215, entry.getUserId()); // hint? + assertFalse(isValidName(entry.getUserName()), entry::getUserName); // hint + assertTrue(entry.isFile()); + assertFalse(entry.isBlockDevice()); + assertFalse(entry.isCharacterDevice()); + assertTrue(entry.isCheckSumOK()); + assertFalse(entry.isDirectory()); + assertFalse(entry.isExtended()); + assertFalse(entry.isFIFO()); + assertFalse(entry.isGlobalPaxHeader()); + assertFalse(entry.isGNULongLinkEntry()); + assertFalse(entry.isGNULongNameEntry()); + assertFalse(entry.isGNUSparse()); + assertFalse(entry.isLink()); + assertFalse(entry.isOldGNUSparse()); + assertFalse(entry.isPaxGNU1XSparse()); + assertFalse(entry.isPaxGNUSparse()); + assertFalse(entry.isPaxHeader()); + assertFalse(entry.isSparse()); + assertFalse(entry.isStarSparse()); + assertTrue(entry.isStreamContiguous()); + assertFalse(entry.isSymbolicLink()); + } + } } diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/Compress700Test.java b/src/test/java/org/apache/commons/compress/archivers/tar/Compress700Test.java index edf561b8c..aa03d2722 100644 --- a/src/test/java/org/apache/commons/compress/archivers/tar/Compress700Test.java +++ b/src/test/java/org/apache/commons/compress/archivers/tar/Compress700Test.java @@ -38,16 +38,14 @@ import java.util.List; import org.apache.commons.compress.archivers.ArchiveStreamFactory; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; /** * Tests https://issues.apache.org/jira/browse/COMPRESS-699 */ public class Compress700Test { - private static final Path PATH = Paths.get("src/test/resources/org/apache/commons/compress/COMPRESS-700/flutter_awesome_buttons-0.1.0-fail.tar"); + private static final Path PATH = Paths.get("src/test/resources/org/apache/commons/compress/COMPRESS-700/flutter_awesome_buttons-0.1.0.tar"); @Test public void testFirstTarArchiveEntry() throws Exception { @@ -187,8 +185,8 @@ public void testListEntries() throws IOException { } } - @Disabled - @Ignore + //@Disabled + //@Ignore @Test public void testTarArchive() throws Exception { try (BufferedInputStream fileInputStream = new BufferedInputStream(Files.newInputStream(PATH))) { diff --git a/src/test/resources/org/apache/commons/compress/COMPRESS-700/flutter_awesome_buttons-0.1.0-fail.tar b/src/test/resources/org/apache/commons/compress/COMPRESS-700/flutter_awesome_buttons-0.1.0.tar similarity index 100% rename from src/test/resources/org/apache/commons/compress/COMPRESS-700/flutter_awesome_buttons-0.1.0-fail.tar rename to src/test/resources/org/apache/commons/compress/COMPRESS-700/flutter_awesome_buttons-0.1.0.tar