From 1e95220dd8d8d1b0d83bce15dc4a398479ef31b8 Mon Sep 17 00:00:00 2001 From: vito Date: Thu, 8 Aug 2019 10:51:35 +0800 Subject: [PATCH] REPORT-20372 CVE-2017-5644 fix --- .../v2/org/apache/poi/POIXMLTypeLoader.java | 55 ++++++++++++---- .../org/apache/poi/util/DocumentHelper.java | 65 +++++++++++++++++-- 2 files changed, 105 insertions(+), 15 deletions(-) diff --git a/fine-poi/src/com/fr/third/v2/org/apache/poi/POIXMLTypeLoader.java b/fine-poi/src/com/fr/third/v2/org/apache/poi/POIXMLTypeLoader.java index f9203a570..9776713d2 100644 --- a/fine-poi/src/com/fr/third/v2/org/apache/poi/POIXMLTypeLoader.java +++ b/fine-poi/src/com/fr/third/v2/org/apache/poi/POIXMLTypeLoader.java @@ -18,9 +18,11 @@ package com.fr.third.v2.org.apache.poi; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.Reader; +import java.io.StringReader; import java.net.URL; import java.util.Collections; import java.util.HashMap; @@ -28,6 +30,7 @@ import java.util.Map; import javax.xml.stream.XMLStreamReader; +import com.fr.third.v2.org.apache.poi.util.DocumentHelper; import com.fr.third.v2.org.apache.xmlbeans.SchemaType; import com.fr.third.v2.org.apache.xmlbeans.XmlBeans; import com.fr.third.v2.org.apache.xmlbeans.XmlException; @@ -35,19 +38,26 @@ import com.fr.third.v2.org.apache.xmlbeans.XmlObject; import com.fr.third.v2.org.apache.xmlbeans.XmlOptions; import com.fr.third.v2.org.apache.xmlbeans.xml.stream.XMLInputStream; import com.fr.third.v2.org.apache.xmlbeans.xml.stream.XMLStreamException; +import org.w3c.dom.Document; import org.w3c.dom.Node; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; @SuppressWarnings("deprecation") public class POIXMLTypeLoader { public static final XmlOptions DEFAULT_XML_OPTIONS; + static { DEFAULT_XML_OPTIONS = new XmlOptions(); DEFAULT_XML_OPTIONS.setSaveOuter(); DEFAULT_XML_OPTIONS.setUseDefaultNamespace(); DEFAULT_XML_OPTIONS.setSaveAggressiveNamespaces(); DEFAULT_XML_OPTIONS.setCharacterEncoding("UTF-8"); - DEFAULT_XML_OPTIONS.setLoadEntityBytesLimit(4096); + // Piccolo is disabled for POI builts, i.e. JAXP is used for parsing + // so only user code using XmlObject/XmlToken.Factory.parse + // directly can bypass the entity check, which is probably unlikely (... and not within our responsibility :)) + // DEFAULT_XML_OPTIONS.setLoadEntityBytesLimit(4096); Map map = new HashMap(); map.put("http://schemas.openxmlformats.org/drawingml/2006/main", "a"); @@ -66,10 +76,9 @@ public class POIXMLTypeLoader { map.put("urn:schemas-microsoft-com:vml", "v"); DEFAULT_XML_OPTIONS.setSaveSuggestedPrefixes(Collections.unmodifiableMap(map)); } - + private static XmlOptions getXmlOptions(XmlOptions options) { - XmlOptions opt = (options == null) ? DEFAULT_XML_OPTIONS : options; - return opt; + return options == null ? DEFAULT_XML_OPTIONS : options; } public static XmlObject newInstance(SchemaType type, XmlOptions options) { @@ -77,19 +86,38 @@ public class POIXMLTypeLoader { } public static XmlObject parse(String xmlText, SchemaType type, XmlOptions options) throws XmlException { - return XmlBeans.getContextTypeLoader().parse(xmlText, type, getXmlOptions(options)); + try { + return parse(new StringReader(xmlText), type, options); + } catch (IOException e) { + throw new XmlException("Unable to parse xml bean", e); + } } public static XmlObject parse(File file, SchemaType type, XmlOptions options) throws XmlException, IOException { - return XmlBeans.getContextTypeLoader().parse(file, type, getXmlOptions(options)); + InputStream is = new FileInputStream(file); + try { + return parse(is, type, options); + } finally { + is.close(); + } } public static XmlObject parse(URL file, SchemaType type, XmlOptions options) throws XmlException, IOException { - return XmlBeans.getContextTypeLoader().parse(file, type, getXmlOptions(options)); + InputStream is = file.openStream(); + try { + return parse(is, type, options); + } finally { + is.close(); + } } public static XmlObject parse(InputStream jiois, SchemaType type, XmlOptions options) throws XmlException, IOException { - return XmlBeans.getContextTypeLoader().parse(jiois, type, getXmlOptions(options)); + try { + Document doc = DocumentHelper.readDocument(jiois); + return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options)); + } catch (SAXException e) { + throw new IOException("Unable to parse xml bean", e); + } } public static XmlObject parse(XMLStreamReader xsr, SchemaType type, XmlOptions options) throws XmlException { @@ -97,7 +125,12 @@ public class POIXMLTypeLoader { } public static XmlObject parse(Reader jior, SchemaType type, XmlOptions options) throws XmlException, IOException { - return XmlBeans.getContextTypeLoader().parse(jior, type, getXmlOptions(options)); + try { + Document doc = DocumentHelper.readDocument(new InputSource(jior)); + return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options)); + } catch (SAXException e) { + throw new XmlException("Unable to parse xml bean", e); + } } public static XmlObject parse(Node node, SchemaType type, XmlOptions options) throws XmlException { @@ -107,8 +140,8 @@ public class POIXMLTypeLoader { public static XmlObject parse(XMLInputStream xis, SchemaType type, XmlOptions options) throws XmlException, XMLStreamException { return XmlBeans.getContextTypeLoader().parse(xis, type, getXmlOptions(options)); } - - public static XMLInputStream newValidatingXMLInputStream ( XMLInputStream xis, SchemaType type, XmlOptions options ) throws XmlException, XMLStreamException { + + public static XMLInputStream newValidatingXMLInputStream(XMLInputStream xis, SchemaType type, XmlOptions options) throws XmlException, XMLStreamException { return XmlBeans.getContextTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options)); } } diff --git a/fine-poi/src/com/fr/third/v2/org/apache/poi/util/DocumentHelper.java b/fine-poi/src/com/fr/third/v2/org/apache/poi/util/DocumentHelper.java index f6d7b5bb1..d9bba2eb8 100644 --- a/fine-poi/src/com/fr/third/v2/org/apache/poi/util/DocumentHelper.java +++ b/fine-poi/src/com/fr/third/v2/org/apache/poi/util/DocumentHelper.java @@ -29,12 +29,55 @@ import javax.xml.stream.events.Namespace; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; public final class DocumentHelper { private static POILogger logger = POILogFactory.getLogger(DocumentHelper.class); - private DocumentHelper() {} + private DocumentHelper() { + } + + private static class DocHelperErrorHandler implements ErrorHandler { + + public void warning(SAXParseException exception) throws SAXException { + printError(POILogger.WARN, exception); + } + + public void error(SAXParseException exception) throws SAXException { + printError(POILogger.ERROR, exception); + } + + public void fatalError(SAXParseException exception) throws SAXException { + printError(POILogger.FATAL, exception); + throw exception; + } + + /** + * Prints the error message. + */ + private void printError(int type, SAXParseException ex) { + StringBuilder sb = new StringBuilder(); + + String systemId = ex.getSystemId(); + if (systemId != null) { + int index = systemId.lastIndexOf('/'); + if (index != -1) + systemId = systemId.substring(index + 1); + sb.append(systemId); + } + sb.append(':'); + sb.append(ex.getLineNumber()); + sb.append(':'); + sb.append(ex.getColumnNumber()); + sb.append(": "); + sb.append(ex.getMessage()); + + logger.log(type, sb.toString(), ex); + } + } /** * Creates a new document builder, with sensible defaults @@ -43,6 +86,7 @@ public final class DocumentHelper { try { DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); documentBuilder.setEntityResolver(SAXHelper.IGNORING_ENTITY_RESOLVER); + documentBuilder.setErrorHandler(new DocHelperErrorHandler()); return documentBuilder; } catch (ParserConfigurationException e) { throw new IllegalStateException("cannot create a DocumentBuilder", e); @@ -50,6 +94,7 @@ public final class DocumentHelper { } private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + static { documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setValidating(false); @@ -66,10 +111,10 @@ public final class DocumentHelper { logger.log(POILogger.WARN, "Cannot set SAX feature because outdated XML parser in classpath", feature, ame); } } - + private static void trySetXercesSecurityManager(DocumentBuilderFactory documentBuilderFactory) { // Try built-in JVM one first, standalone if not - for (String securityManagerClassName : new String[] { + for (String securityManagerClassName : new String[]{ "com.sun.org.apache.xerces.internal.util.SecurityManager", "org.apache.xerces.util.SecurityManager" }) { @@ -89,13 +134,25 @@ public final class DocumentHelper { /** * Parses the given stream via the default (sensible) * DocumentBuilder + * * @param inp Stream to read the XML data from - * @return the parsed Document + * @return the parsed Document */ public static Document readDocument(InputStream inp) throws IOException, SAXException { return newDocumentBuilder().parse(inp); } + /** + * Parses the given stream via the default (sensible) + * DocumentBuilder + * + * @param inp sax source to read the XML data from + * @return the parsed Document + */ + public static Document readDocument(InputSource inp) throws IOException, SAXException { + return newDocumentBuilder().parse(inp); + } + // must only be used to create empty documents, do not use it for parsing! private static final DocumentBuilder documentBuilderSingleton = newDocumentBuilder();