mirror of
https://github.com/apache/poi.git
synced 2026-02-27 20:40:08 +08:00
check xwpf node depth (#869)
* check xwpf node depth * Update TestAllFiles.java * Update TestAllFiles.java
This commit is contained in:
parent
fa573c72da
commit
b50ce609ca
@ -97,6 +97,9 @@ public class TestAllFiles {
|
||||
"poifs/60320-protected.xlsx",
|
||||
"poifs/protected_sha512.xlsx",
|
||||
|
||||
// stress docs
|
||||
"document/deep-table-cell.docx",
|
||||
|
||||
// NOTE: Expected failures should usually be added in file "stress.xls" instead
|
||||
// of being listed here in order to also verify the expected exception details!
|
||||
};
|
||||
|
||||
@ -19,7 +19,7 @@ package org.apache.poi.ooxml;
|
||||
/**
|
||||
* Indicates a generic OOXML error.
|
||||
*/
|
||||
public final class POIXMLException extends RuntimeException{
|
||||
public final class POIXMLException extends RuntimeException {
|
||||
/**
|
||||
* Create a new {@code POIXMLException} with no
|
||||
* detail message.
|
||||
|
||||
@ -23,22 +23,11 @@ import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Optional;
|
||||
import java.util.Spliterator;
|
||||
import java.util.*;
|
||||
import javax.xml.namespace.QName;
|
||||
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.poi.POIException;
|
||||
import org.apache.poi.logging.PoiLogManager;
|
||||
import org.apache.poi.common.usermodel.PictureType;
|
||||
import org.apache.poi.ooxml.POIXMLDocument;
|
||||
@ -61,6 +50,7 @@ import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.Internal;
|
||||
import org.apache.poi.util.Removal;
|
||||
import org.apache.poi.util.XMLHelper;
|
||||
import org.apache.poi.wp.usermodel.HeaderFooterType;
|
||||
import org.apache.poi.xddf.usermodel.chart.XDDFChart;
|
||||
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
|
||||
@ -105,6 +95,7 @@ import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument;
|
||||
@SuppressWarnings("unused")
|
||||
public class XWPFDocument extends POIXMLDocument implements Document, IBody {
|
||||
private static final Logger LOG = PoiLogManager.getLogger(XWPFDocument.class);
|
||||
private static final int MAX_NODE_DEPTH = 1000;
|
||||
|
||||
protected List<XWPFFooter> footers = new ArrayList<>();
|
||||
protected List<XWPFHeader> headers = new ArrayList<>();
|
||||
@ -214,6 +205,13 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
|
||||
doc = DocumentDocument.Factory.parse(stream, DEFAULT_XML_OPTIONS);
|
||||
ctDocument = doc.getDocument();
|
||||
}
|
||||
final int nodeDepth = XMLHelper.getDepthOfChildNodes(ctDocument.getDomNode(), MAX_NODE_DEPTH);
|
||||
if (nodeDepth > MAX_NODE_DEPTH) {
|
||||
throw new IOException(String.format(Locale.ROOT,
|
||||
"The document is too complex, it has a node depth of %s, which exceeds the maximum allowed of %s",
|
||||
nodeDepth,
|
||||
MAX_NODE_DEPTH));
|
||||
}
|
||||
|
||||
initFootnotes();
|
||||
|
||||
@ -304,6 +302,8 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
|
||||
}
|
||||
}
|
||||
initHyperlinks();
|
||||
} catch (POIException e) {
|
||||
throw new IOException(e);
|
||||
} catch (XmlException e) {
|
||||
throw new POIXMLException(e);
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import javax.crypto.Cipher;
|
||||
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.zip.ZipFile;
|
||||
import org.apache.poi.POIDataSamples;
|
||||
import org.apache.poi.POIException;
|
||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||
import org.apache.poi.openxml4j.opc.PackagePartName;
|
||||
@ -274,4 +275,13 @@ class TestXWPFBugs {
|
||||
assertEquals(STJcTable.END, tbl5.getCTTbl().getTblPr().getJc().xgetVal().getEnumValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeepTableCell() throws Exception {
|
||||
// Document contains a table with nested cells.
|
||||
IOException ex = assertThrows(IOException.class,
|
||||
() -> XWPFTestDataSamples.openSampleDocument("deep-table-cell.docx"));
|
||||
assertInstanceOf(POIException.class, ex.getCause());
|
||||
assertTrue(ex.getMessage().contains("Node depth exceeds maximum supported depth"));
|
||||
}
|
||||
}
|
||||
|
||||
59
poi/src/main/java/org/apache/poi/POIException.java
Normal file
59
poi/src/main/java/org/apache/poi/POIException.java
Normal file
@ -0,0 +1,59 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
/**
|
||||
* Indicates a generic POI exception. This is not commonly used in POI
|
||||
* but this is intended to be a base class for some new POI exceptions.
|
||||
* Historically, POI has used {@link RuntimeException} for most of its
|
||||
* exceptions, but this is not a good practice. This class is a checked
|
||||
* class that extends {@link Exception} so needs to be explictly
|
||||
* caught or declared in the method signature.
|
||||
*
|
||||
* @since POI 5.5.0
|
||||
*/
|
||||
public class POIException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create a new {@code POIException} with the specified message.
|
||||
*
|
||||
* @param msg The error message for the exception.
|
||||
*/
|
||||
public POIException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code POIException} with the specified cause.
|
||||
*
|
||||
* @param cause the cause of this exception
|
||||
*/
|
||||
public POIException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code POIException} with the specified message and cause.
|
||||
*
|
||||
* @param msg The error message for the exception.
|
||||
* @param cause the cause of this exception
|
||||
*/
|
||||
public POIException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@ import static javax.xml.stream.XMLOutputFactory.IS_REPAIRING_NAMESPACES;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
@ -49,7 +50,9 @@ import javax.xml.validation.SchemaFactory;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogBuilder;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.poi.POIException;
|
||||
import org.apache.poi.logging.PoiLogManager;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
@ -77,7 +80,6 @@ public final class XMLHelper {
|
||||
"org.apache.xerces.util.SecurityManager"
|
||||
};
|
||||
|
||||
|
||||
private static final Logger LOG = PoiLogManager.getLogger(XMLHelper.class);
|
||||
private static long lastLog;
|
||||
|
||||
@ -253,6 +255,38 @@ public final class XMLHelper {
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the depth of the DOM tree starting from the given node.
|
||||
*
|
||||
* @param node the node to check
|
||||
* @param maxSupportedDepth the maximum supported depth of the DOM tree
|
||||
* @return the depth
|
||||
* @throws POIException if the depth exceeds <code>maxSupportedDepth</code>
|
||||
*/
|
||||
public static int getDepthOfChildNodes(final Node node, final int maxSupportedDepth) throws POIException {
|
||||
return getDepthOfChildNodes(node, maxSupportedDepth, 0);
|
||||
}
|
||||
|
||||
private static int getDepthOfChildNodes(final Node node, final int maxSupportedDepth,
|
||||
final int nodeDepth) throws POIException {
|
||||
final int currentDepth = nodeDepth + 1;
|
||||
int maxDepth = currentDepth;
|
||||
Node child = node.getFirstChild();
|
||||
while (child != null) {
|
||||
int childDepth = getDepthOfChildNodes(child, maxSupportedDepth, currentDepth);
|
||||
if (childDepth > maxDepth) {
|
||||
maxDepth = childDepth;
|
||||
if (maxDepth > maxSupportedDepth) {
|
||||
throw new POIException(String.format(Locale.ROOT,
|
||||
"Node depth exceeds maximum supported depth of %s" ,
|
||||
maxSupportedDepth));
|
||||
}
|
||||
}
|
||||
child = child.getNextSibling();
|
||||
}
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
private static Object _xercesSecurityManager;
|
||||
private static volatile boolean _xercesSecurityManagerSet = false;
|
||||
|
||||
|
||||
BIN
test-data/document/deep-table-cell.docx
Normal file
BIN
test-data/document/deep-table-cell.docx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user