This is an automated email from the ASF dual-hosted git repository. bodewig 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 61615b3 COMPRESS-575 harden parser for PAX 0.0 sparse headers 61615b3 is described below commit 61615b31c57fd96e122a70bd823e155117422328 Author: Stefan Bodewig <bode...@apache.org> AuthorDate: Sat May 1 10:11:57 2021 +0200 COMPRESS-575 harden parser for PAX 0.0 sparse headers --- .../commons/compress/archivers/tar/TarUtils.java | 28 ++++++- .../compress/archivers/tar/TarUtilsTest.java | 92 +++++++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java index 5b7fb28..ea4741a 100644 --- a/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java +++ b/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java @@ -695,6 +695,10 @@ public class TarUtils { + got); } // Drop trailing NL + if (rest[rest.length - 1] != '\n') { + throw new IOException("Failed to read Paxheader." + + "Value should end with a newline"); + } final String value = new String(rest, 0, restLen - 1, StandardCharsets.UTF_8); headers.put(keyword, value); @@ -705,7 +709,16 @@ public class TarUtils { // previous GNU.sparse.offset header but but no numBytes sparseHeaders.add(new TarArchiveStructSparse(offset, 0)); } - offset = Long.valueOf(value); + try { + offset = Long.valueOf(value); + } catch (NumberFormatException ex) { + throw new IOException("Failed to read Paxheader." + + "GNU.sparse.offset contains a non-numeric value"); + } + if (offset < 0) { + throw new IOException("Failed to read Paxheader." + + "GNU.sparse.offset contains negative value"); + } } // for 0.0 PAX Headers @@ -714,7 +727,18 @@ public class TarUtils { throw new IOException("Failed to read Paxheader." + "GNU.sparse.offset is expected before GNU.sparse.numbytes shows up."); } - sparseHeaders.add(new TarArchiveStructSparse(offset, Long.parseLong(value))); + long numbytes; + try { + numbytes = Long.parseLong(value); + } catch (NumberFormatException ex) { + throw new IOException("Failed to read Paxheader." + + "GNU.sparse.numbytes contains a non-numeric value."); + } + if (numbytes < 0) { + throw new IOException("Failed to read Paxheader." + + "GNU.sparse.numbytes contains negative value"); + } + sparseHeaders.add(new TarArchiveStructSparse(offset, numbytes)); offset = null; } } diff --git a/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java b/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java index 446a059..4e04f06 100644 --- a/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java +++ b/src/test/java/org/apache/commons/compress/archivers/tar/TarUtilsTest.java @@ -23,7 +23,10 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.commons.compress.archivers.zip.ZipEncoding; @@ -31,9 +34,12 @@ import org.apache.commons.compress.archivers.zip.ZipEncodingHelper; import org.apache.commons.compress.utils.ByteUtils; import org.apache.commons.compress.utils.CharsetNames; import org.apache.commons.compress.utils.IOUtils; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import static org.apache.commons.compress.AbstractTestCase.getFile; +import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -41,6 +47,8 @@ import static org.junit.Assert.fail; public class TarUtilsTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); @Test public void testName(){ @@ -449,8 +457,9 @@ public class TarUtilsTest { assertEquals(ae, headers.get("path")); } - @Test(expected = IOException.class) + @Test public void testParseTarWithSpecialPaxHeaders() throws IOException { + thrown.expect(IOException.class); try (InputStream in = Files.newInputStream(getFile("COMPRESS-530.tar").toPath()); TarArchiveInputStream archive = new TarArchiveInputStream(in)) { archive.getNextEntry(); @@ -458,4 +467,85 @@ public class TarUtilsTest { } } + @Test + public void readPaxHeaderWithoutTrailingNewline() throws Exception { + thrown.expect(IOException.class); + thrown.expectMessage(startsWith("Failed to read Paxheader")); + TarUtils.parsePaxHeaders( + new ByteArrayInputStream("30 atime=1321711775.9720594634".getBytes(StandardCharsets.UTF_8)), + null, Collections.emptyMap()); + } + + @Test + public void readPax00SparseHeader() throws Exception { + final String header = "23 GNU.sparse.offset=0\n" + + "26 GNU.sparse.numbytes=10\n"; + final List<TarArchiveStructSparse> sparseHeaders = new ArrayList<>(); + TarUtils.parsePaxHeaders( + new ByteArrayInputStream(header.getBytes(StandardCharsets.UTF_8)), + sparseHeaders, Collections.emptyMap()); + assertEquals(1, sparseHeaders.size()); + assertEquals(0, sparseHeaders.get(0).getOffset()); + assertEquals(10, sparseHeaders.get(0).getNumbytes()); + } + + @Test + public void readPax00SparseHeaderMakesNumbytesOptional() throws Exception { + final String header = "23 GNU.sparse.offset=0\n" + + "24 GNU.sparse.offset=10\n"; + final List<TarArchiveStructSparse> sparseHeaders = new ArrayList<>(); + TarUtils.parsePaxHeaders( + new ByteArrayInputStream(header.getBytes(StandardCharsets.UTF_8)), + sparseHeaders, Collections.emptyMap()); + assertEquals(2, sparseHeaders.size()); + assertEquals(0, sparseHeaders.get(0).getOffset()); + assertEquals(0, sparseHeaders.get(0).getNumbytes()); + assertEquals(10, sparseHeaders.get(1).getOffset()); + assertEquals(0, sparseHeaders.get(1).getNumbytes()); + } + + @Test + public void readPax00SparseHeaderRejectsNonNumericOffset() throws Exception { + thrown.expect(IOException.class); + thrown.expectMessage(startsWith("Failed to read Paxheader")); + final String header = "23 GNU.sparse.offset=a\n" + + "26 GNU.sparse.numbytes=10\n"; + TarUtils.parsePaxHeaders( + new ByteArrayInputStream(header.getBytes(StandardCharsets.UTF_8)), + null, Collections.emptyMap()); + } + + @Test + public void readPax00SparseHeaderRejectsNonNumericNumbytes() throws Exception { + thrown.expect(IOException.class); + thrown.expectMessage(startsWith("Failed to read Paxheader")); + final String header = "23 GNU.sparse.offset=0\n" + + "26 GNU.sparse.numbytes=1a\n"; + TarUtils.parsePaxHeaders( + new ByteArrayInputStream(header.getBytes(StandardCharsets.UTF_8)), + null, Collections.emptyMap()); + } + + @Test + public void readPax00SparseHeaderRejectsNegativeOffset() throws Exception { + thrown.expect(IOException.class); + thrown.expectMessage(startsWith("Failed to read Paxheader")); + final String header = "24 GNU.sparse.offset=-1\n" + + "26 GNU.sparse.numbytes=10\n"; + TarUtils.parsePaxHeaders( + new ByteArrayInputStream(header.getBytes(StandardCharsets.UTF_8)), + null, Collections.emptyMap()); + } + + @Test + public void readPax00SparseHeaderRejectsNegativeNumbytes() throws Exception { + thrown.expect(IOException.class); + thrown.expectMessage(startsWith("Failed to read Paxheader")); + final String header = "23 GNU.sparse.offset=0\n" + + "26 GNU.sparse.numbytes=-1\n"; + TarUtils.parsePaxHeaders( + new ByteArrayInputStream(header.getBytes(StandardCharsets.UTF_8)), + null, Collections.emptyMap()); + } + }