Skip to content

Commit bb22cd4

Browse files
Sergey PatrikeevSergey Patrikeev
Sergey Patrikeev
authored and
Sergey Patrikeev
committed
Add ability to limit output size for zip unarchiver as a way of protection against zip bombs.
1 parent 40f072b commit bb22cd4

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

src/main/java/org/codehaus/plexus/archiver/zip/AbstractZipUnArchiver.java

+32-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.apache.commons.compress.archivers.zip.ZipFile;
3030
import org.codehaus.plexus.archiver.AbstractUnArchiver;
3131
import org.codehaus.plexus.archiver.ArchiverException;
32+
import org.apache.commons.io.input.BoundedInputStream;
33+
import org.apache.commons.io.input.CountingInputStream;
3234
import org.codehaus.plexus.components.io.resources.PlexusIoResource;
3335

3436
/**
@@ -42,6 +44,8 @@ public abstract class AbstractZipUnArchiver
4244

4345
private String encoding = "UTF8";
4446

47+
private long maxOutputSize = Long.MAX_VALUE;
48+
4549
public AbstractZipUnArchiver()
4650
{
4751
}
@@ -66,6 +70,21 @@ public void setEncoding( String encoding )
6670
this.encoding = encoding;
6771
}
6872

73+
/**
74+
* Set maximum allowed size of the produced output.
75+
*
76+
* It may be used as a protection against <a href="https://en.wikipedia.org/wiki/Zip_bomb">zip bombs</a>.
77+
*
78+
* @param maxOutputSize max size of the produced output, in bytes. Must be greater than 0
79+
* @throws IllegalArgumentException if specified output size is less or equal to 0
80+
*/
81+
public void setMaxOutputSize( long maxOutputSize ) {
82+
if ( maxOutputSize <= 0 ) {
83+
throw new IllegalArgumentException( "Invalid max output size specified: " + maxOutputSize );
84+
}
85+
this.maxOutputSize = maxOutputSize;
86+
}
87+
6988
private static class ZipEntryFileInfo
7089
implements PlexusIoResource
7190
{
@@ -181,6 +200,7 @@ protected void execute( final String path, final File outputDirectory )
181200
getLogger().debug( "Expanding: " + getSourceFile() + " into " + outputDirectory );
182201
try ( ZipFile zipFile = new ZipFile( getSourceFile(), encoding, true ) )
183202
{
203+
long remainingSpace = maxOutputSize;
184204
final Enumeration<ZipArchiveEntry> e = zipFile.getEntriesInPhysicalOrder();
185205

186206
while ( e.hasMoreElements() )
@@ -196,10 +216,18 @@ protected void execute( final String path, final File outputDirectory )
196216
{
197217
try ( InputStream in = zipFile.getInputStream( ze ) )
198218
{
199-
extractFile( getSourceFile(), outputDirectory, in,
200-
ze.getName(), new Date( ze.getTime() ), ze.isDirectory(),
201-
ze.getUnixMode() != 0 ? ze.getUnixMode() : null,
202-
resolveSymlink( zipFile, ze ), getFileMappers() );
219+
BoundedInputStream bis = new BoundedInputStream( in, remainingSpace + 1 );
220+
CountingInputStream cis = new CountingInputStream( bis );
221+
extractFile( getSourceFile(), outputDirectory, cis,
222+
ze.getName(), new Date( ze.getTime() ), ze.isDirectory(),
223+
ze.getUnixMode() != 0 ? ze.getUnixMode() : null,
224+
resolveSymlink( zipFile, ze ), getFileMappers() );
225+
226+
remainingSpace -= cis.getByteCount();
227+
if ( remainingSpace < 0 )
228+
{
229+
throw new ArchiverException("Maximum output size limit reached");
230+
}
203231
}
204232
}
205233
}

src/test/java/org/codehaus/plexus/archiver/zip/ZipUnArchiverTest.java

+25
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,31 @@ public void testExtractingZipWithEntryOutsideDestDirThrowsException()
214214
assertTrue( ex.getMessage().startsWith( "Entry is outside of the target directory" ) );
215215
}
216216

217+
public void testZipOutputSizeException()
218+
throws Exception
219+
{
220+
Exception ex = null;
221+
String s = "target/zip-size-tests";
222+
File testZip = new File( getBasedir(), "src/test/jars/test.zip" );
223+
File outputDirectory = new File( getBasedir(), s );
224+
225+
FileUtils.deleteDirectory( outputDirectory );
226+
227+
try
228+
{
229+
ZipUnArchiver zu = getZipUnArchiver( testZip );
230+
zu.setMaxOutputSize(10L);
231+
zu.extract( "", outputDirectory );
232+
}
233+
catch ( Exception e )
234+
{
235+
ex = e;
236+
}
237+
238+
assertNotNull( ex );
239+
assertTrue( ex.getMessage().startsWith( "Maximum output size limit reached" ) );
240+
}
241+
217242
private ZipArchiver getZipArchiver()
218243
{
219244
try

0 commit comments

Comments
 (0)