Bug 56865 - Limit number of bytes (by counting them) while opening office docs

Bug 50090 - 'zip' bomb prevention

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1687148 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2015-06-23 23:39:07 +00:00
parent 5adb9cd513
commit 5a42a0cd05
11 changed files with 426 additions and 49 deletions

View File

@ -23,6 +23,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.apache.poi.openxml4j.opc.internal.ZipHelper;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.util.CellReference;
@ -165,7 +166,7 @@ public class BigGridDemo {
* @param out the stream to write the result to
*/
private static void substitute(File zipfile, File tmpfile, String entry, OutputStream out) throws IOException {
ZipFile zip = new ZipFile(zipfile);
ZipFile zip = ZipHelper.openZipFile(zipfile);
ZipOutputStream zos = new ZipOutputStream(out);

View File

@ -39,6 +39,7 @@ import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.poi.openxml4j.opc.internal.ZipHelper;
import org.apache.poi.util.IOUtils;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
@ -82,7 +83,7 @@ public class OOXMLPrettyPrint {
IOException, TransformerException, ParserConfigurationException {
System.out.println("Reading zip-file " + file + " and writing pretty-printed XML to " + outFile);
ZipFile zipFile = new ZipFile(file);
ZipFile zipFile = ZipHelper.openZipFile(file);
try {
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
try {

View File

@ -41,6 +41,8 @@ import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
import org.apache.poi.openxml4j.util.ZipEntrySource;
import org.apache.poi.openxml4j.util.ZipFileZipEntrySource;
import org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.openxml4j.util.ZipSecureFile.ThresholdInputStream;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.TempFile;
@ -85,9 +87,9 @@ public final class ZipPackage extends Package {
@SuppressWarnings("deprecation")
ZipPackage(InputStream in, PackageAccess access) throws IOException {
super(access);
this.zipArchive = new ZipInputStreamZipEntrySource(
new ZipInputStream(in)
);
InputStream zis = new ZipInputStream(in);
ThresholdInputStream tis = ZipSecureFile.addThreshold(zis);
this.zipArchive = new ZipInputStreamZipEntrySource(tis);
}
/**

View File

@ -29,6 +29,7 @@ import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.ZipPackage;
import org.apache.poi.openxml4j.util.ZipSecureFile;
public final class ZipHelper {
@ -154,7 +155,7 @@ public final class ZipHelper {
return null;
}
return new ZipFile(file);
return new ZipSecureFile(file);
}
/**
@ -171,6 +172,6 @@ public final class ZipHelper {
return null;
}
return new ZipFile(f);
return new ZipSecureFile(f);
}
}

View File

@ -26,6 +26,8 @@ import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.poi.openxml4j.util.ZipSecureFile.ThresholdInputStream;
/**
* Provides a way to get at all the ZipEntries
* from a ZipInputStream, as many times as required.
@ -43,7 +45,7 @@ public class ZipInputStreamZipEntrySource implements ZipEntrySource {
* We'll then eat lots of memory, but be able to
* work with the entries at-will.
*/
public ZipInputStreamZipEntrySource(ZipInputStream inp) throws IOException {
public ZipInputStreamZipEntrySource(ThresholdInputStream inp) throws IOException {
zipEntries = new ArrayList<FakeZipEntry>();
boolean going = true;
@ -105,7 +107,7 @@ public class ZipInputStreamZipEntrySource implements ZipEntrySource {
public static class FakeZipEntry extends ZipEntry {
private byte[] data;
public FakeZipEntry(ZipEntry entry, ZipInputStream inp) throws IOException {
public FakeZipEntry(ZipEntry entry, InputStream inp) throws IOException {
super(entry.getName());
// Grab the de-compressed contents for later

View File

@ -0,0 +1,227 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.openxml4j.util;
import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
* This class wraps a {@link ZipFile} in order to check the
* entries for <a href="https://en.wikipedia.org/wiki/Zip_bomb">zip bombs</a>
* while reading the archive.
* If a {@link ZipInputStream} is directly used, the wrapper
* can be applied via {@link #addThreshold(InputStream)}.
* The alert limits can be globally defined via {@link #setMaxEntrySize(long)}
* and {@link #setMinInflateRatio(double)}.
*/
public class ZipSecureFile extends ZipFile {
private static POILogger logger = POILogFactory.getLogger(ZipSecureFile.class);
private static double MIN_INFLATE_RATIO = 0.01d;
private static long MAX_ENTRY_SIZE = 0xFFFFFFFFl;
/**
* Sets the ratio between de- and inflated bytes to detect zipbomb.
* It defaults to 1% (= 0.01d), i.e. when the compression is better than
* 1% for any given read package part, the parsing will fail
*
* @param ratio the ratio between de- and inflated bytes to detect zipbomb
*/
public static void setMinInflateRatio(double ratio) {
MIN_INFLATE_RATIO = ratio;
}
/**
* Sets the maximum file size of a single zip entry. It defaults to 4GB,
* i.e. the 32-bit zip format maximum.
*
* @param maxEntrySize the max. file size of a single zip entry
*/
public static void setMaxEntrySize(long maxEntrySize) {
if (maxEntrySize < 0 || maxEntrySize > 0xFFFFFFFFl) {
throw new IllegalArgumentException("Max entry size is bounded [0-4GB].");
}
MAX_ENTRY_SIZE = maxEntrySize;
}
public ZipSecureFile(File file, Charset charset) throws IOException {
super(file, charset);
}
public ZipSecureFile(File file, int mode, Charset charset) throws IOException {
super(file, mode, charset);
}
public ZipSecureFile(File file, int mode) throws IOException {
super(file, mode);
}
public ZipSecureFile(File file) throws ZipException, IOException {
super(file);
}
public ZipSecureFile(String name, Charset charset) throws IOException {
super(name, charset);
}
public ZipSecureFile(String name) throws IOException {
super(name);
}
/**
* Returns an input stream for reading the contents of the specified
* zip file entry.
*
* <p> Closing this ZIP file will, in turn, close all input
* streams that have been returned by invocations of this method.
*
* @param entry the zip file entry
* @return the input stream for reading the contents of the specified
* zip file entry.
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
* @throws IllegalStateException if the zip file has been closed
*/
public InputStream getInputStream(ZipEntry entry) throws IOException {
InputStream zipIS = super.getInputStream(entry);
return addThreshold(zipIS);
}
public static ThresholdInputStream addThreshold(InputStream zipIS) throws IOException {
ThresholdInputStream newInner;
if (zipIS instanceof InflaterInputStream) {
try {
Field f = FilterInputStream.class.getDeclaredField("in");
f.setAccessible(true);
InputStream oldInner = (InputStream)f.get(zipIS);
newInner = new ThresholdInputStream(oldInner, null);
f.set(zipIS, newInner);
} catch (Exception ex) {
logger.log(POILogger.WARN, "SecurityManager doesn't allow manipulation via reflection for zipbomb detection - continue with original input stream", ex);
newInner = null;
}
} else {
// the inner stream is a ZipFileInputStream, i.e. the data wasn't compressed
newInner = null;
}
return new ThresholdInputStream(zipIS, newInner);
}
public static class ThresholdInputStream extends PushbackInputStream {
long counter = 0;
ThresholdInputStream cis;
public ThresholdInputStream(InputStream is, ThresholdInputStream cis) {
super(is,1);
this.cis = cis;
}
public int read() throws IOException {
int b = in.read();
if (b > -1) advance(1);
return b;
}
public int read(byte b[], int off, int len) throws IOException {
int cnt = in.read(b, off, len);
if (cnt > -1) advance(cnt);
return cnt;
}
public long skip(long n) throws IOException {
counter = 0;
return in.skip(n);
}
public synchronized void reset() throws IOException {
counter = 0;
in.reset();
}
public void advance(int advance) throws IOException {
counter += advance;
// check the file size first, in case we are working on uncompressed streams
if (counter < MAX_ENTRY_SIZE) {
if (cis == null) return;
double ratio = (double)cis.counter/(double)counter;
if (ratio > MIN_INFLATE_RATIO) return;
}
throw new IOException("Zip bomb detected! Exiting.");
}
public ZipEntry getNextEntry() throws IOException {
if (!(in instanceof ZipInputStream)) {
throw new UnsupportedOperationException("underlying stream is not a ZipInputStream");
}
counter = 0;
return ((ZipInputStream)in).getNextEntry();
}
public void closeEntry() throws IOException {
if (!(in instanceof ZipInputStream)) {
throw new UnsupportedOperationException("underlying stream is not a ZipInputStream");
}
counter = 0;
((ZipInputStream)in).closeEntry();
}
public void unread(int b) throws IOException {
if (!(in instanceof PushbackInputStream)) {
throw new UnsupportedOperationException("underlying stream is not a PushbackInputStream");
}
if (--counter < 0) counter = 0;
((PushbackInputStream)in).unread(b);
}
public void unread(byte[] b, int off, int len) throws IOException {
if (!(in instanceof PushbackInputStream)) {
throw new UnsupportedOperationException("underlying stream is not a PushbackInputStream");
}
counter -= len;
if (--counter < 0) counter = 0;
((PushbackInputStream)in).unread(b, off, len);
}
public int available() throws IOException {
return in.available();
}
public boolean markSupported() {
return in.markSupported();
}
public synchronized void mark(int readlimit) {
in.mark(readlimit);
}
}
}

View File

@ -24,6 +24,7 @@ import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.poi.openxml4j.opc.internal.ZipHelper;
import org.apache.poi.util.IOUtils;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
@ -38,7 +39,7 @@ public final class XSSFDump {
public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++) {
System.out.println("Dumping " + args[i]);
ZipFile zip = new ZipFile(args[i]);
ZipFile zip = ZipHelper.openZipFile(args[i]);
try {
dump(zip);
} finally {

View File

@ -32,6 +32,7 @@ import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.internal.ZipHelper;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
@ -334,7 +335,7 @@ public class SXSSFWorkbook implements Workbook
}
private void injectData(File zipfile, OutputStream out) throws IOException
{
ZipFile zip = new ZipFile(zipfile);
ZipFile zip = ZipHelper.openZipFile(zipfile);
try
{
ZipOutputStream zos = new ZipOutputStream(out);

View File

@ -17,28 +17,24 @@
package org.apache.poi.openxml4j.opc;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.poi.openxml4j.opc.compliance.AllOpenXML4JComplianceTests;
import org.apache.poi.openxml4j.opc.internal.AllOpenXML4JInternalTests;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestContentType.class
, TestFileHelper.class
, TestListParts.class
, TestPackage.class
, TestPackageCoreProperties.class
, TestPackagePartName.class
, TestPackageThumbnail.class
, TestPackagingURIHelper.class
, TestRelationships.class
, AllOpenXML4JComplianceTests.class
, AllOpenXML4JInternalTests.class
})
public final class AllOpenXML4JTests {
public static Test suite() {
TestSuite suite = new TestSuite(AllOpenXML4JTests.class.getName());
suite.addTestSuite(TestContentType.class);
suite.addTestSuite(TestFileHelper.class);
suite.addTestSuite(TestListParts.class);
suite.addTestSuite(TestPackage.class);
suite.addTestSuite(TestPackageCoreProperties.class);
suite.addTestSuite(TestPackagePartName.class);
suite.addTestSuite(TestPackageThumbnail.class);
suite.addTestSuite(TestPackagingURIHelper.class);
suite.addTestSuite(TestRelationships.class);
suite.addTest(AllOpenXML4JComplianceTests.suite());
suite.addTest(AllOpenXML4JInternalTests.suite());
return suite;
}
}

View File

@ -17,6 +17,14 @@
package org.apache.poi.openxml4j.opc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@ -25,35 +33,47 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import junit.framework.TestCase;
import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.OpenXML4JTestDataSamples;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
import org.apache.poi.openxml4j.opc.internal.FileHelper;
import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart;
import org.apache.poi.openxml4j.opc.internal.ZipHelper;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.TempFile;
import org.junit.Ignore;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public final class TestPackage extends TestCase {
public final class TestPackage {
private static final POILogger logger = POILogFactory.getLogger(TestPackage.class);
/**
* Test that just opening and closing the file doesn't alter the document.
*/
public void testOpenSave() throws Exception {
@Test
public void openSave() throws Exception {
String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx");
@ -75,7 +95,8 @@ public final class TestPackage extends TestCase {
* Test that when we create a new Package, we give it
* the correct default content types
*/
public void testCreateGetsContentTypes() throws Exception {
@Test
public void createGetsContentTypes() throws Exception {
File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx");
// Zap the target file, in case of an earlier run
@ -107,7 +128,8 @@ public final class TestPackage extends TestCase {
/**
* Test package creation.
*/
public void testCreatePackageAddPart() throws Exception {
@Test
public void createPackageAddPart() throws Exception {
File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestCreatePackageTMP.docx");
File expectedFile = OpenXML4JTestDataSamples.getSampleFile("TestCreatePackageOUTPUT.docx");
@ -153,7 +175,8 @@ public final class TestPackage extends TestCase {
* document and another part, save and re-load and
* have everything setup as expected
*/
public void testCreatePackageWithCoreDocument() throws Exception {
@Test
public void createPackageWithCoreDocument() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OPCPackage pkg = OPCPackage.create(baos);
@ -247,7 +270,8 @@ public final class TestPackage extends TestCase {
/**
* Test package opening.
*/
public void testOpenPackage() throws Exception {
@Test
public void openPackage() throws Exception {
File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestOpenPackageTMP.docx");
File inputFile = OpenXML4JTestDataSamples.getSampleFile("TestOpenPackageINPUT.docx");
@ -306,7 +330,8 @@ public final class TestPackage extends TestCase {
* OutputStream, in addition to the normal writing
* to a file
*/
public void testSaveToOutputStream() throws Exception {
@Test
public void saveToOutputStream() throws Exception {
String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageOpenSaveTMP.docx");
@ -334,7 +359,8 @@ public final class TestPackage extends TestCase {
* simple InputStream, in addition to the normal
* reading from a file
*/
public void testOpenFromInputStream() throws Exception {
@Test
public void openFromInputStream() throws Exception {
String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
FileInputStream finp = new FileInputStream(originalFile);
@ -353,7 +379,9 @@ public final class TestPackage extends TestCase {
/**
* TODO: fix and enable
*/
public void disabled_testRemovePartRecursive() throws Exception {
@Test
@Ignore
public void removePartRecursive() throws Exception {
String originalFile = OpenXML4JTestDataSamples.getSampleFileName("TestPackageCommon.docx");
File targetFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveOUTPUT.docx");
File tempFile = OpenXML4JTestDataSamples.getOutputFile("TestPackageRemovePartRecursiveTMP.docx");
@ -369,7 +397,8 @@ public final class TestPackage extends TestCase {
assertTrue(targetFile.delete());
}
public void testDeletePart() throws InvalidFormatException {
@Test
public void deletePart() throws InvalidFormatException {
TreeMap<PackagePartName, String> expectedValues;
TreeMap<PackagePartName, String> values;
@ -426,7 +455,8 @@ public final class TestPackage extends TestCase {
p.revert();
}
public void testDeletePartRecursive() throws InvalidFormatException {
@Test
public void deletePartRecursive() throws InvalidFormatException {
TreeMap<PackagePartName, String> expectedValues;
TreeMap<PackagePartName, String> values;
@ -468,7 +498,8 @@ public final class TestPackage extends TestCase {
* Test that we can open a file by path, and then
* write changes to it.
*/
public void testOpenFileThenOverwrite() throws Exception {
@Test
public void openFileThenOverwrite() throws Exception {
File tempFile = TempFile.createTempFile("poiTesting","tmp");
File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx");
FileHelper.copyFile(origFile, tempFile);
@ -505,7 +536,8 @@ public final class TestPackage extends TestCase {
* Test that we can open a file by path, save it
* to another file, then delete both
*/
public void testOpenFileThenSaveDelete() throws Exception {
@Test
public void openFileThenSaveDelete() throws Exception {
File tempFile = TempFile.createTempFile("poiTesting","tmp");
File tempFile2 = TempFile.createTempFile("poiTesting","tmp");
File origFile = OpenXML4JTestDataSamples.getSampleFile("TestPackageCommon.docx");
@ -529,7 +561,8 @@ public final class TestPackage extends TestCase {
return (ContentTypeManager)f.get(pkg);
}
public void testGetPartsByName() throws Exception {
@Test
public void getPartsByName() throws Exception {
String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ_WRITE);
@ -553,7 +586,8 @@ public final class TestPackage extends TestCase {
}
}
public void testGetPartSize() throws Exception {
@Test
public void getPartSize() throws Exception {
String filepath = OpenXML4JTestDataSamples.getSampleFileName("sample.docx");
OPCPackage pkg = OPCPackage.open(filepath, PackageAccess.READ);
try {
@ -585,7 +619,8 @@ public final class TestPackage extends TestCase {
}
}
public void testReplaceContentType() throws Exception {
@Test
public void replaceContentType() throws Exception {
InputStream is = OpenXML4JTestDataSamples.openSampleStream("sample.xlsx");
OPCPackage p = OPCPackage.open(is);
@ -603,4 +638,113 @@ public final class TestPackage extends TestCase {
assertFalse(mgr.isContentTypeRegister("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"));
assertTrue(mgr.isContentTypeRegister("application/vnd.ms-excel.sheet.macroEnabled.main+xml"));
}
@Test(expected=IOException.class)
public void zipBombCreateAndHandle() throws Exception {
// #50090 / #56865
ZipFile zipFile = ZipHelper.openZipFile(OpenXML4JTestDataSamples.getSampleFile("sample.xlsx"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream append = new ZipOutputStream(bos);
// first, copy contents from existing war
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry e2 = entries.nextElement();
ZipEntry e = new ZipEntry(e2.getName());
e.setTime(e2.getTime());
e.setComment(e2.getComment());
e.setSize(e2.getSize());
append.putNextEntry(e);
if (!e.isDirectory()) {
InputStream is = zipFile.getInputStream(e);
if (e.getName().equals("[Content_Types].xml")) {
ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
IOUtils.copy(is, bos2);
long size = bos2.size()-"</Types>".length();
append.write(bos2.toByteArray(), 0, (int)size);
byte spam[] = new byte[0x7FFF];
for (int i=0; i<spam.length; i++) spam[i] = ' ';
while (size < 0x7FFF0000) {
append.write(spam);
size += spam.length;
}
append.write("</Types>".getBytes());
size += 8;
e.setSize(size);
} else {
IOUtils.copy(is, append);
}
}
append.closeEntry();
}
append.close();
zipFile.close();
byte buf[] = bos.toByteArray();
bos = null;
Workbook wb = WorkbookFactory.create(new ByteArrayInputStream(buf));
wb.getSheetAt(0);
wb.close();
}
@Test
public void zipBombCheckSizes() throws Exception {
File file = OpenXML4JTestDataSamples.getSampleFile("sample.xlsx");
try {
double min_ratio = Double.MAX_VALUE;
long max_size = 0;
ZipFile zf = ZipHelper.openZipFile(file);
Enumeration<? extends ZipEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = entries.nextElement();
double ratio = (double)ze.getCompressedSize() / (double)ze.getSize();
min_ratio = Math.min(min_ratio, ratio);
max_size = Math.max(max_size, ze.getSize());
}
zf.close();
// use values close to, but within the limits
ZipSecureFile.setMinInflateRatio(min_ratio-0.002);
ZipSecureFile.setMaxEntrySize(max_size+1);
Workbook wb = WorkbookFactory.create(file);
wb.close();
// check ratio ouf of bounds
ZipSecureFile.setMinInflateRatio(min_ratio+0.002);
try {
wb = WorkbookFactory.create(file);
wb.close();
// this is a bit strange, as there will be different exceptions thrown
// depending if this executed via "ant test" or within eclipse
// maybe a difference in JDK ...
} catch (InvalidFormatException e) {
assertEquals("Zip bomb detected! Exiting.", e.getMessage());
} catch (POIXMLException e) {
InvocationTargetException t = (InvocationTargetException)e.getCause();
IOException t2 = (IOException)t.getTargetException();
assertEquals("Zip bomb detected! Exiting.", t2.getMessage());
}
// check max entry size ouf of bounds
ZipSecureFile.setMinInflateRatio(min_ratio-0.002);
ZipSecureFile.setMaxEntrySize(max_size-1);
try {
wb = WorkbookFactory.create(file, null, true);
wb.close();
} catch (InvalidFormatException e) {
assertEquals("Zip bomb detected! Exiting.", e.getMessage());
} catch (POIXMLException e) {
InvocationTargetException t = (InvocationTargetException)e.getCause();
IOException t2 = (IOException)t.getTargetException();
assertEquals("Zip bomb detected! Exiting.", t2.getMessage());
}
} finally {
// reset otherwise a lot of ooxml tests will fail
ZipSecureFile.setMinInflateRatio(0.01d);
ZipSecureFile.setMaxEntrySize(0xFFFFFFFFl);
}
}
}

View File

@ -516,6 +516,7 @@ public final class ExcelFileFormatDocFunctionExtractor {
ps.println("#Columns: (index, name, minParams, maxParams, returnClass, paramClasses, isVolatile, hasFootnote )");
ps.println("");
try {
// can't use ZipHelper here, because its in a different module
ZipFile zf = new ZipFile(effDocFile);
InputStream is = zf.getInputStream(zf.getEntry("content.xml"));
extractFunctionData(new FunctionDataCollector(ps), is);