From 80cb805eb1488ba3a16c427866fa8ae1f52ff0c5 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 10 Jun 2018 10:15:30 +0000 Subject: [PATCH 1/2] use safe XML parsers git-svn-id: https://svn.apache.org/repos/asf/xmlbeans/trunk@1833260 13f79535-47bb-0310-9956-ffa450edef68 --- .../xmlbeans/impl/common/DocumentHelper.java | 165 ++++++++++++++++++ .../xmlbeans/impl/common/LoadSaveUtils.java | 6 +- .../xmlbeans/impl/common}/NullLogger.java | 4 +- .../xmlbeans/impl/common}/SAXHelper.java | 6 +- .../apache/xmlbeans/impl/common/Sax2Dom.java | 9 +- .../xmlbeans/impl/common}/XBLogFactory.java | 4 +- .../xmlbeans/impl/common}/XBLogger.java | 4 +- .../apache/xmlbeans/impl/store/Locale.java | 24 +-- 8 files changed, 189 insertions(+), 33 deletions(-) create mode 100644 src/common/org/apache/xmlbeans/impl/common/DocumentHelper.java rename src/{store/org/apache/xmlbeans/impl/store => common/org/apache/xmlbeans/impl/common}/NullLogger.java (95%) rename src/{store/org/apache/xmlbeans/impl/store => common/org/apache/xmlbeans/impl/common}/SAXHelper.java (96%) rename src/{store/org/apache/xmlbeans/impl/store => common/org/apache/xmlbeans/impl/common}/XBLogFactory.java (97%) rename src/{store/org/apache/xmlbeans/impl/store => common/org/apache/xmlbeans/impl/common}/XBLogger.java (97%) diff --git a/src/common/org/apache/xmlbeans/impl/common/DocumentHelper.java b/src/common/org/apache/xmlbeans/impl/common/DocumentHelper.java new file mode 100644 index 00000000..8c487644 --- /dev/null +++ b/src/common/org/apache/xmlbeans/impl/common/DocumentHelper.java @@ -0,0 +1,165 @@ +/* Copyright 2004-2018 The Apache Software Foundation + * + * Licensed 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.xmlbeans.impl.common; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +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 XBLogger logger = XBLogFactory.getLogger(DocumentHelper.class); + + private DocumentHelper() {} + + private static class DocHelperErrorHandler implements ErrorHandler { + + public void warning(SAXParseException exception) throws SAXException { + printError(XBLogger.WARN, exception); + } + + public void error(SAXParseException exception) throws SAXException { + printError(XBLogger.ERROR, exception); + } + + public void fatalError(SAXParseException exception) throws SAXException { + printError(XBLogger.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 + * + * @throws IllegalStateException If creating the DocumentBuilder fails, e.g. + * due to {@link ParserConfigurationException}. + */ + public static synchronized DocumentBuilder newDocumentBuilder() { + 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); + } + } + + private static final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + static { + documentBuilderFactory.setNamespaceAware(true); + documentBuilderFactory.setValidating(false); + trySetSAXFeature(documentBuilderFactory, XMLConstants.FEATURE_SECURE_PROCESSING, true); + trySetXercesSecurityManager(documentBuilderFactory); + } + + private static void trySetSAXFeature(DocumentBuilderFactory dbf, String feature, boolean enabled) { + try { + dbf.setFeature(feature, enabled); + } catch (Exception e) { + logger.log(XBLogger.WARN, "SAX Feature unsupported", feature, e); + } catch (AbstractMethodError ame) { + logger.log(XBLogger.WARN, "Cannot set SAX feature because outdated XML parser in classpath", feature, ame); + } + } + + private static void trySetXercesSecurityManager(DocumentBuilderFactory dbf) { + // Try built-in JVM one first, standalone if not + for (String securityManagerClassName : new String[]{ + //"com.sun.org.apache.xerces.internal.util.SecurityManager", + "org.apache.xerces.util.SecurityManager" + }) { + try { + Object mgr = Class.forName(securityManagerClassName).newInstance(); + Method setLimit = mgr.getClass().getMethod("setEntityExpansionLimit", Integer.TYPE); + setLimit.invoke(mgr, 4096); + dbf.setAttribute("http://apache.org/xml/properties/security-manager", mgr); + // Stop once one can be setup without error + return; + } catch (ClassNotFoundException e) { + // continue without log, this is expected in some setups + } catch (Throwable e) { // NOSONAR - also catch things like NoClassDefError here + logger.log(XBLogger.WARN, "SAX Security Manager could not be setup", e); + } + } + + // separate old version of Xerces not found => use the builtin way of setting the property + dbf.setAttribute("http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit", 4096); + } + + /** + * Parses the given stream via the default (sensible) + * DocumentBuilder + * @param inp Stream to read the XML data from + * @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(); + + /** + * Creates a new DOM Document + */ + public static synchronized Document createDocument() { + return documentBuilderSingleton.newDocument(); + } +} diff --git a/src/common/org/apache/xmlbeans/impl/common/LoadSaveUtils.java b/src/common/org/apache/xmlbeans/impl/common/LoadSaveUtils.java index 74b52743..a80deff9 100644 --- a/src/common/org/apache/xmlbeans/impl/common/LoadSaveUtils.java +++ b/src/common/org/apache/xmlbeans/impl/common/LoadSaveUtils.java @@ -22,7 +22,6 @@ package org.apache.xmlbeans.impl.common; import org.w3c.dom.Document; import org.xml.sax.SAXException; -import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParser; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamWriter; @@ -40,10 +39,7 @@ public class LoadSaveUtils public static Document xmlText2GenericDom(InputStream is, Document emptyDoc) throws SAXException, ParserConfigurationException, IOException { - SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setNamespaceAware(true); - - SAXParser parser = factory.newSAXParser(); + SAXParser parser = SAXHelper.saxFactory.newSAXParser(); Sax2Dom handler = new Sax2Dom(emptyDoc); diff --git a/src/store/org/apache/xmlbeans/impl/store/NullLogger.java b/src/common/org/apache/xmlbeans/impl/common/NullLogger.java similarity index 95% rename from src/store/org/apache/xmlbeans/impl/store/NullLogger.java rename to src/common/org/apache/xmlbeans/impl/common/NullLogger.java index aca8d1d5..6b5874a4 100644 --- a/src/store/org/apache/xmlbeans/impl/store/NullLogger.java +++ b/src/common/org/apache/xmlbeans/impl/common/NullLogger.java @@ -1,4 +1,4 @@ -/* Copyright 2017 The Apache Software Foundation +/* Copyright 2017, 2018 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.apache.xmlbeans.impl.store; +package org.apache.xmlbeans.impl.common; /** * A logger class that strives to make it as easy as possible for diff --git a/src/store/org/apache/xmlbeans/impl/store/SAXHelper.java b/src/common/org/apache/xmlbeans/impl/common/SAXHelper.java similarity index 96% rename from src/store/org/apache/xmlbeans/impl/store/SAXHelper.java rename to src/common/org/apache/xmlbeans/impl/common/SAXHelper.java index 67fb3a0e..71bed2dc 100644 --- a/src/store/org/apache/xmlbeans/impl/store/SAXHelper.java +++ b/src/common/org/apache/xmlbeans/impl/common/SAXHelper.java @@ -1,4 +1,4 @@ -/* Copyright 2017 The Apache Software Foundation +/* Copyright 2017, 2018 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.apache.xmlbeans.impl.store; +package org.apache.xmlbeans.impl.common; import java.io.IOException; import java.io.StringReader; @@ -57,7 +57,7 @@ public final class SAXHelper { } }; - private static final SAXParserFactory saxFactory; + static final SAXParserFactory saxFactory; static { saxFactory = SAXParserFactory.newInstance(); saxFactory.setValidating(false); diff --git a/src/common/org/apache/xmlbeans/impl/common/Sax2Dom.java b/src/common/org/apache/xmlbeans/impl/common/Sax2Dom.java index 67294bb8..989eafcb 100644 --- a/src/common/org/apache/xmlbeans/impl/common/Sax2Dom.java +++ b/src/common/org/apache/xmlbeans/impl/common/Sax2Dom.java @@ -28,7 +28,6 @@ import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.ext.LexicalHandler; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.DocumentBuilderFactory; import java.util.Stack; import java.util.Vector; @@ -49,9 +48,7 @@ public class Sax2Dom public Sax2Dom() throws ParserConfigurationException { - final DocumentBuilderFactory factory = - DocumentBuilderFactory.newInstance(); - _document = factory.newDocumentBuilder().newDocument(); + _document = DocumentHelper.newDocumentBuilder().newDocument(); _root = _document; } @@ -68,9 +65,7 @@ public class Sax2Dom } else { - final DocumentBuilderFactory factory = - DocumentBuilderFactory.newInstance(); - _document = factory.newDocumentBuilder().newDocument(); + _document = DocumentHelper.newDocumentBuilder().newDocument(); _root = _document; } } diff --git a/src/store/org/apache/xmlbeans/impl/store/XBLogFactory.java b/src/common/org/apache/xmlbeans/impl/common/XBLogFactory.java similarity index 97% rename from src/store/org/apache/xmlbeans/impl/store/XBLogFactory.java rename to src/common/org/apache/xmlbeans/impl/common/XBLogFactory.java index f31d4db7..0afac4d5 100644 --- a/src/store/org/apache/xmlbeans/impl/store/XBLogFactory.java +++ b/src/common/org/apache/xmlbeans/impl/common/XBLogFactory.java @@ -1,4 +1,4 @@ -/* Copyright 2017 The Apache Software Foundation +/* Copyright 2017, 2018 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.apache.xmlbeans.impl.store; +package org.apache.xmlbeans.impl.common; import java.util.HashMap; import java.util.Map; diff --git a/src/store/org/apache/xmlbeans/impl/store/XBLogger.java b/src/common/org/apache/xmlbeans/impl/common/XBLogger.java similarity index 97% rename from src/store/org/apache/xmlbeans/impl/store/XBLogger.java rename to src/common/org/apache/xmlbeans/impl/common/XBLogger.java index fa605112..b1394226 100644 --- a/src/store/org/apache/xmlbeans/impl/store/XBLogger.java +++ b/src/common/org/apache/xmlbeans/impl/common/XBLogger.java @@ -1,4 +1,4 @@ -/* Copyright 2017 The Apache Software Foundation +/* Copyright 2017, 2018 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.apache.xmlbeans.impl.store; +package org.apache.xmlbeans.impl.common; /** * A logger interface that strives to make it as easy as possible for diff --git a/src/store/org/apache/xmlbeans/impl/store/Locale.java b/src/store/org/apache/xmlbeans/impl/store/Locale.java index 1f02a160..4a4d5927 100644 --- a/src/store/org/apache/xmlbeans/impl/store/Locale.java +++ b/src/store/org/apache/xmlbeans/impl/store/Locale.java @@ -1,4 +1,4 @@ -/* Copyright 2004 The Apache Software Foundation +/* Copyright 2004-2018 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ package org.apache.xmlbeans.impl.store; -import org.apache.xmlbeans.XmlErrorCodes; import org.xml.sax.Locator; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; @@ -45,6 +44,7 @@ import java.io.Reader; import java.io.StringReader; import java.io.IOException; +import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamException; @@ -59,15 +59,7 @@ import org.apache.xmlbeans.xml.stream.XMLEvent; import org.apache.xmlbeans.xml.stream.XMLInputStream; import org.apache.xmlbeans.xml.stream.XMLName; -import org.w3c.dom.DOMImplementation; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Node; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; - +import org.apache.xmlbeans.impl.common.SAXHelper; import org.apache.xmlbeans.impl.common.XMLNameHelper; import org.apache.xmlbeans.impl.common.QNameHelper; import org.apache.xmlbeans.impl.common.XmlLocale; @@ -89,10 +81,11 @@ import org.apache.xmlbeans.XmlBeans; import org.apache.xmlbeans.XmlLineNumber; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlCursor.XmlBookmark; -import org.apache.xmlbeans.XmlSaxHandler; +import org.apache.xmlbeans.XmlErrorCodes; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; +import org.apache.xmlbeans.XmlSaxHandler; import org.apache.xmlbeans.SchemaType; import org.apache.xmlbeans.SchemaTypeLoader; import org.apache.xmlbeans.XmlTokenSource; @@ -109,6 +102,13 @@ import org.apache.xmlbeans.impl.values.TypeStoreUserFactory; import org.apache.xmlbeans.impl.piccolo.xml.Piccolo; import org.apache.xmlbeans.impl.piccolo.io.FileFormatException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Element; + public final class Locale implements DOMImplementation, SaajCallback, XmlLocale { -- 2.23.0