Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary Gregory">Add a builder for the TarFile class and deprecate some constructors.</action>
<action type="add" dev="pkarwasz" due-to="Piotr Karwasz">SevenZFile, TarFile, and ZipFile now always close underlying resources when builder or constructor fails.</action>
<action type="add" dev="pkarwasz" due-to="Piotr P. Karwasz">Introduce an ArchiveFile abstraction to unify the APIs of SevenZFile, TarFile, and ZipFile.</action>
<action type="add" dev="pkarwasz" due-to="Piotr P. Karwasz">Add a configurable maxEntryNameLength option to all archivers.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-parent from 85 to 88 #707.</action>
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0.</action>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,48 @@
public abstract class AbstractArchiveBuilder<T, B extends AbstractArchiveBuilder<T, B>>
extends AbstractStreamBuilder<T, B> {

private int maxEntryNameLength = Short.MAX_VALUE;

/**
* Constructs a new instance.
*/
protected AbstractArchiveBuilder() {
// empty
}

/**
* Sets the maximum length, in bytes, of an archive entry name.
*
* <p>Most operating systems and file systems impose relatively small limits on
* file name or path length, which are sufficient for everyday use. By contrast,
* many archive formats permit much longer names: for example, TAR can encode
* names of several gigabytes, while ZIP allows up to 64&nbsp;KiB.</p>
*
* <p>This setting applies an upper bound on entry name length after encoding
* with the {@link #setCharset configured charset}. If an entry name exceeds this
* limit, an {@link ArchiveException} will be thrown during reading.</p>
*
* <p>The default is {@link Short#MAX_VALUE}, which already exceeds the limits
* of most operating systems.</p>
*
* @param maxEntryNameLength The maximum entry name length in bytes; must be positive
* @return {@code this} instance.
* @throws IllegalArgumentException If {@code maxEntryNameLength} is not positive.
*/
public B setMaxEntryNameLength(final int maxEntryNameLength) {
if (maxEntryNameLength <= 0) {
throw new IllegalArgumentException("maxEntryNameLength must be positive");
}
this.maxEntryNameLength = maxEntryNameLength;
return asThis();
}

/**
* Gets the maximum length of an archive entry name.
*
* @return The maximum length of an archive entry name.
*/
public int getMaxEntryNameLength() {
return maxEntryNameLength;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ public Iterator<E> unwrap() {
/** The number of bytes read in this stream. */
private long bytesRead;

private Charset charset;
private final Charset charset;

private final int maxEntryNameLength;

/**
* Constructs a new instance.
Expand All @@ -115,6 +117,7 @@ public ArchiveInputStream() {
protected ArchiveInputStream(final InputStream inputStream, final String charsetName) {
super(inputStream == null ? new NullInputStream() : inputStream);
this.charset = Charsets.toCharset(charsetName);
this.maxEntryNameLength = Short.MAX_VALUE;
}

/**
Expand Down Expand Up @@ -142,6 +145,7 @@ protected ArchiveInputStream(AbstractArchiveBuilder<?, ?> builder) throws IOExce
protected ArchiveInputStream(final InputStream inputStream, final AbstractArchiveBuilder<?, ?> builder) {
super(inputStream);
this.charset = builder.getCharset();
this.maxEntryNameLength = builder.getMaxEntryNameLength();
}

/**
Expand Down Expand Up @@ -226,6 +230,16 @@ public int getCount() {
return (int) bytesRead;
}

/**
* Gets the maximum length of an archive entry name.
*
* @return The maximum length of an archive entry name.
* @since 1.29.0
*/
protected int getMaxEntryNameLength() {
return maxEntryNameLength;
}

/**
* Gets the next Archive Entry in this Stream.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Arrays;
import java.util.regex.Pattern;

import org.apache.commons.compress.MemoryLimitException;
import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
Expand Down Expand Up @@ -245,7 +246,7 @@ public void close() throws IOException {
* @since 1.3
*/
private String getBSDLongName(final String bsdLongName) throws IOException {
final int nameLen = ParsingUtils.parseIntValue(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
final int nameLen = checkEntryNameLength(ParsingUtils.parseIntValue(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN)));
final byte[] name = IOUtils.readRange(in, nameLen);
final int read = name.length;
count(read);
Expand Down Expand Up @@ -277,14 +278,18 @@ private String getExtendedName(final int offset) throws IOException {
}
// Check there is a something to return, otherwise break out of the loop
if (i > offset) {
return ArchiveUtils.toAsciiString(namebuffer, offset, i - offset);
return ArchiveUtils.toAsciiString(namebuffer, offset, checkEntryNameLength(i - offset));
}
break;
}
}
throw new ArchiveException("Failed to read GNU long file name at offset " + offset);
}

private int checkEntryNameLength(final int nameLength) throws ArchiveException, MemoryLimitException {
return ArchiveUtils.checkEntryNameLength(nameLength, getMaxEntryNameLength(), "AR");
}

/**
* Returns the next AR entry in this stream.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.io.input.BoundedInputStream;
import org.apache.commons.io.input.ChecksumInputStream;
Expand Down Expand Up @@ -316,8 +317,8 @@ private LocalFileHeader readLocalFileHeader() throws IOException {

readExtraData(firstHeaderSize, firstHeader, localFileHeader);

localFileHeader.name = readString(basicHeader);
localFileHeader.comment = readString(basicHeader);
localFileHeader.name = readEntryName(basicHeader);
localFileHeader.comment = readComment(basicHeader);

final ArrayList<byte[]> extendedHeaders = new ArrayList<>();
int extendedHeaderSize;
Expand Down Expand Up @@ -376,8 +377,8 @@ private MainHeader readMainHeader() throws IOException {
firstHeader.readUnsignedByte();
}

header.name = readString(basicHeader);
header.comment = readString(basicHeader);
header.name = readEntryName(basicHeader);
header.comment = readComment(basicHeader);

final int extendedHeaderSize = read16(dis);
if (extendedHeaderSize > 0) {
Expand All @@ -402,13 +403,23 @@ private byte[] readRange(final InputStream in, final int len) throws IOException
return b;
}

private String readString(final DataInputStream dataIn) throws IOException {
private String readComment(DataInputStream dataIn) throws IOException {
return new String(readString(dataIn).toByteArray(), getCharset());
}

private String readEntryName(DataInputStream dataIn) throws IOException {
final ByteArrayOutputStream buffer = readString(dataIn);
ArchiveUtils.checkEntryNameLength(buffer.size(), getMaxEntryNameLength(), "ARJ");
return new String(buffer.toByteArray(), getCharset());
}

private ByteArrayOutputStream readString(DataInputStream dataIn) throws IOException {
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
int nextByte;
while ((nextByte = dataIn.readUnsignedByte()) != 0) {
buffer.write(nextByte);
}
return buffer.toString(getCharset().name());
return buffer;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,10 @@ private long readBinaryLong(final int length, final boolean swapHalfWord) throws
return CpioUtil.byteArray2long(tmp, swapHalfWord);
}

private String readCString(final int length) throws IOException {
private String readEntryName(int lengthWithNull) throws IOException {
final int length = ArchiveUtils.checkEntryNameLength(lengthWithNull - 1, getMaxEntryNameLength(), "CPIO");
// don't include trailing NUL in file name to decode
final byte[] tmpBuffer = readRange(length - 1);
final byte[] tmpBuffer = readRange(length);
if (in.read() == -1) {
throw new EOFException();
}
Expand Down Expand Up @@ -471,7 +472,7 @@ private CpioArchiveEntry readNewEntry(final boolean hasCrc) throws IOException {
throw new ArchiveException("Found illegal entry with negative name length");
}
newEntry.setChksum(readAsciiLong(8, 16));
final String name = readCString(ArchiveException.toIntExact(namesize));
final String name = readEntryName(ArchiveException.toIntExact(namesize));
newEntry.setName(name);
if (CpioUtil.fileType(mode) == 0 && !name.equals(CPIO_TRAILER)) {
throw new ArchiveException(
Expand Down Expand Up @@ -505,7 +506,7 @@ private CpioArchiveEntry readOldAsciiEntry() throws IOException {
if (ret.getSize() < 0) {
throw new ArchiveException("Found illegal entry with negative length");
}
final String name = readCString(ArchiveException.toIntExact(nameSize));
final String name = readEntryName(ArchiveException.toIntExact(nameSize));
ret.setName(name);
if (CpioUtil.fileType(mode) == 0 && !name.equals(CPIO_TRAILER)) {
throw new ArchiveException(
Expand Down Expand Up @@ -535,7 +536,7 @@ private CpioArchiveEntry readOldBinaryEntry(final boolean swapHalfWord) throws I
if (oldEntry.getSize() < 0) {
throw new ArchiveException("Found illegal entry with negative length");
}
final String name = readCString(ArchiveException.toIntExact(nameSize));
final String name = readEntryName(ArchiveException.toIntExact(nameSize));
oldEntry.setName(name);
if (CpioUtil.fileType(mode) == 0 && !name.equals(CPIO_TRAILER)) {
throw new ArchiveException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
import java.util.Queue;
import java.util.Stack;

import org.apache.commons.compress.MemoryLimitException;
import org.apache.commons.compress.archivers.AbstractArchiveBuilder;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.IOUtils;

/**
Expand Down Expand Up @@ -253,7 +255,7 @@ public DumpArchiveEntry getNextEntry() throws IOException {

// is there anything in the queue?
if (!queue.isEmpty()) {
return queue.remove();
return checkEntry(queue.remove());
}

while (entry == null) {
Expand Down Expand Up @@ -336,7 +338,7 @@ public DumpArchiveEntry getNextEntry() throws IOException {
entry.setSimpleName(names.get(entry.getIno()).getName());
entry.setOffset(filepos);

return entry;
return checkEntry(entry);
}

/**
Expand Down Expand Up @@ -585,4 +587,8 @@ private void readDirectoryEntry(DumpArchiveEntry entry) throws IOException {
}
}

private DumpArchiveEntry checkEntry(DumpArchiveEntry entry) throws ArchiveException, MemoryLimitException {
ArchiveUtils.checkEntryNameLength(entry.getName().length(), getMaxEntryNameLength(), "DUMP");
return entry;
}
}
Loading