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:
parent
057768994e
commit
c0cb680292
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
227
src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
Normal file
227
src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user