diff --git a/src/main/java/org/codehaus/plexus/archiver/zip/AbstractZipUnArchiver.java b/src/main/java/org/codehaus/plexus/archiver/zip/AbstractZipUnArchiver.java
index f35c64666..6319bb755 100644
--- a/src/main/java/org/codehaus/plexus/archiver/zip/AbstractZipUnArchiver.java
+++ b/src/main/java/org/codehaus/plexus/archiver/zip/AbstractZipUnArchiver.java
@@ -29,7 +29,8 @@
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.codehaus.plexus.archiver.AbstractUnArchiver;
import org.codehaus.plexus.archiver.ArchiverException;
-import org.codehaus.plexus.components.io.filemappers.FileMapper;
+import org.apache.commons.io.input.BoundedInputStream;
+import org.apache.commons.io.input.CountingInputStream;
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
/**
@@ -43,6 +44,8 @@ public abstract class AbstractZipUnArchiver
private String encoding = "UTF8";
+ private long maxOutputSize = Long.MAX_VALUE;
+
public AbstractZipUnArchiver()
{
}
@@ -67,6 +70,21 @@ public void setEncoding( String encoding )
this.encoding = encoding;
}
+ /**
+ * Set maximum allowed size of the produced output.
+ *
+ * It may be used as a protection against zip bombs.
+ *
+ * @param maxOutputSize max size of the produced output, in bytes. Must be greater than 0
+ * @throws IllegalArgumentException if specified output size is less or equal to 0
+ */
+ public void setMaxOutputSize( long maxOutputSize ) {
+ if ( maxOutputSize <= 0 ) {
+ throw new IllegalArgumentException( "Invalid max output size specified: " + maxOutputSize );
+ }
+ this.maxOutputSize = maxOutputSize;
+ }
+
private static class ZipEntryFileInfo
implements PlexusIoResource
{
@@ -159,31 +177,7 @@ public boolean isExisting()
protected void execute()
throws ArchiverException
{
- getLogger().debug( "Expanding: " + getSourceFile() + " into " + getDestDirectory() );
- try ( ZipFile zf = new ZipFile( getSourceFile(), encoding, true ) )
- {
- final Enumeration e = zf.getEntriesInPhysicalOrder();
- while ( e.hasMoreElements() )
- {
- final ZipArchiveEntry ze = e.nextElement();
- final ZipEntryFileInfo fileInfo = new ZipEntryFileInfo( zf, ze );
- if ( isSelected( fileInfo.getName(), fileInfo ) )
- {
- try ( InputStream in = zf.getInputStream( ze ) )
- {
- extractFileIfIncluded( getSourceFile(), getDestDirectory(), in, fileInfo.getName(),
- new Date( ze.getTime() ), ze.isDirectory(),
- ze.getUnixMode() != 0 ? ze.getUnixMode() : null,
- resolveSymlink( zf, ze ), getFileMappers() );
- }
- }
- }
- getLogger().debug( "expand complete" );
- }
- catch ( final IOException ioe )
- {
- throw new ArchiverException( "Error while expanding " + getSourceFile().getAbsolutePath(), ioe );
- }
+ execute("", getDestDirectory());
}
private String resolveSymlink( ZipFile zf, ZipArchiveEntry ze )
@@ -199,20 +193,14 @@ private String resolveSymlink( ZipFile zf, ZipArchiveEntry ze )
}
}
- private void extractFileIfIncluded( final File sourceFile, final File destDirectory, final InputStream inputStream,
- final String name, final Date time, final boolean isDirectory,
- final Integer mode, String symlinkDestination, final FileMapper[] fileMappers )
- throws IOException, ArchiverException
- {
- extractFile( sourceFile, destDirectory, inputStream, name, time, isDirectory, mode, symlinkDestination, fileMappers );
- }
-
@Override
protected void execute( final String path, final File outputDirectory )
throws ArchiverException
{
+ getLogger().debug( "Expanding: " + getSourceFile() + " into " + outputDirectory );
try ( ZipFile zipFile = new ZipFile( getSourceFile(), encoding, true ) )
{
+ long remainingSpace = maxOutputSize;
final Enumeration e = zipFile.getEntriesInPhysicalOrder();
while ( e.hasMoreElements() )
@@ -228,13 +216,22 @@ protected void execute( final String path, final File outputDirectory )
{
try ( InputStream in = zipFile.getInputStream( ze ) )
{
- extractFileIfIncluded( getSourceFile(), outputDirectory, in,
- ze.getName(), new Date( ze.getTime() ), ze.isDirectory(),
- ze.getUnixMode() != 0 ? ze.getUnixMode() : null,
- resolveSymlink( zipFile, ze ), getFileMappers() );
+ BoundedInputStream bis = new BoundedInputStream( in, remainingSpace + 1 );
+ CountingInputStream cis = new CountingInputStream( bis );
+ extractFile( getSourceFile(), outputDirectory, cis,
+ ze.getName(), new Date( ze.getTime() ), ze.isDirectory(),
+ ze.getUnixMode() != 0 ? ze.getUnixMode() : null,
+ resolveSymlink( zipFile, ze ), getFileMappers() );
+
+ remainingSpace -= cis.getByteCount();
+ if ( remainingSpace < 0 )
+ {
+ throw new ArchiverException("Maximum output size limit reached");
+ }
}
}
}
+ getLogger().debug( "expand complete" );
}
catch ( final IOException ioe )
{
diff --git a/src/test/java/org/codehaus/plexus/archiver/zip/ZipUnArchiverTest.java b/src/test/java/org/codehaus/plexus/archiver/zip/ZipUnArchiverTest.java
index ea46a5aae..fbb95371d 100644
--- a/src/test/java/org/codehaus/plexus/archiver/zip/ZipUnArchiverTest.java
+++ b/src/test/java/org/codehaus/plexus/archiver/zip/ZipUnArchiverTest.java
@@ -214,6 +214,31 @@ public void testExtractingZipWithEntryOutsideDestDirThrowsException()
assertTrue( ex.getMessage().startsWith( "Entry is outside of the target directory" ) );
}
+ public void testZipOutputSizeException()
+ throws Exception
+ {
+ Exception ex = null;
+ String s = "target/zip-size-tests";
+ File testZip = new File( getBasedir(), "src/test/jars/test.zip" );
+ File outputDirectory = new File( getBasedir(), s );
+
+ FileUtils.deleteDirectory( outputDirectory );
+
+ try
+ {
+ ZipUnArchiver zu = getZipUnArchiver( testZip );
+ zu.setMaxOutputSize(10L);
+ zu.extract( "", outputDirectory );
+ }
+ catch ( Exception e )
+ {
+ ex = e;
+ }
+
+ assertNotNull( ex );
+ assertTrue( ex.getMessage().startsWith( "Maximum output size limit reached" ) );
+ }
+
private ZipArchiver getZipArchiver()
{
try