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