Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ARTEMIS-4077 Add an option to disable XML external entity processing
- Loading branch information
Showing
19 changed files
with
569 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
265 changes: 265 additions & 0 deletions
265
artemis-commons/src/main/java/org/apache/activemq/artemis/utils/XmlProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
/* | ||
* 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.activemq.artemis.utils; | ||
|
||
import javax.xml.XMLConstants; | ||
import javax.xml.parsers.DocumentBuilder; | ||
import javax.xml.parsers.DocumentBuilderFactory; | ||
import javax.xml.parsers.ParserConfigurationException; | ||
import javax.xml.stream.XMLInputFactory; | ||
import javax.xml.stream.XMLStreamException; | ||
import javax.xml.stream.XMLStreamReader; | ||
import javax.xml.transform.Source; | ||
import javax.xml.transform.Transformer; | ||
import javax.xml.transform.TransformerConfigurationException; | ||
import javax.xml.transform.TransformerFactory; | ||
import javax.xml.validation.Schema; | ||
import javax.xml.validation.SchemaFactory; | ||
import javax.xml.validation.Validator; | ||
import java.io.InputStream; | ||
import java.io.Reader; | ||
import java.net.URL; | ||
import java.util.Map; | ||
|
||
import org.w3c.dom.ls.LSInput; | ||
import org.xml.sax.SAXException; | ||
|
||
public class XmlProvider { | ||
public static final String ARTEMIS_DISABLE_XXE_PROPERTY = "artemis.disableXxe"; | ||
|
||
public static final String XINCLUDE_AWARE_PROPERTY = "XINCLUDE_AWARE"; | ||
public static final String NAMESPACE_AWARE_PROPERTY = "NAMESPACE_AWARE"; | ||
public static final String IGNORE_COMMENTS_PROPERTY = "IGNORE_COMMENTS"; | ||
public static final String IGNORE_ELEMENT_CONTENT_WHITESPACE_PROPERTY = "IGNORE_ELEMENT_CONTENT_WHITESPACE"; | ||
|
||
private static final String ACTIVEMQ_CORE_NS = "urn:activemq:core"; | ||
private static final String ACTIVEMQ_JMS_NS = "urn:activemq:jms"; | ||
|
||
private static final String ARTEMIS_XML_SCHEMA_SID = "xml.xsd"; | ||
private static final String ARTEMIS_CONFIGURATION_SCHEMA_SID = "artemis-configuration.xsd"; | ||
private static final String ARTEMIS_JMS_SCHEMA_SID = "artemis-jms.xsd"; | ||
|
||
private static final String ARTEMIS_SCHEMA_BASE_URL = "schema/"; | ||
private static final String ARTEMIS_XML_SCHEMA_URL = ARTEMIS_SCHEMA_BASE_URL + ARTEMIS_XML_SCHEMA_SID; | ||
private static final String ARTEMIS_CONFIGURATION_SCHEMA_URL = ARTEMIS_SCHEMA_BASE_URL + ARTEMIS_CONFIGURATION_SCHEMA_SID; | ||
private static final String ARTEMIS_JMS_SCHEMA_URL = ARTEMIS_SCHEMA_BASE_URL + ARTEMIS_JMS_SCHEMA_SID; | ||
|
||
private static boolean xxeEnabled = !"".equals(System.getProperty(ARTEMIS_DISABLE_XXE_PROPERTY)) && | ||
!Boolean.parseBoolean(System.getProperty(ARTEMIS_DISABLE_XXE_PROPERTY, Boolean.FALSE.toString())); | ||
|
||
public static boolean isXxeEnabled() { | ||
return xxeEnabled; | ||
} | ||
|
||
public static void setXxeEnabled(boolean enabled) { | ||
xxeEnabled = enabled; | ||
} | ||
|
||
public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException { | ||
return newDocumentBuilder(null, null); | ||
} | ||
|
||
public static DocumentBuilder newDocumentBuilder(Map<String, Boolean> features, Map<String, Boolean> properties) throws ParserConfigurationException { | ||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | ||
|
||
if (features != null) { | ||
for (Map.Entry<String, Boolean> feature : features.entrySet()) { | ||
factory.setFeature(feature.getKey(), feature.getValue()); | ||
} | ||
} | ||
|
||
if (properties != null) { | ||
for (Map.Entry<String, Boolean> property : properties.entrySet()) { | ||
if (XINCLUDE_AWARE_PROPERTY.equals(property.getKey())) { | ||
factory.setXIncludeAware(property.getValue()); | ||
} else if (NAMESPACE_AWARE_PROPERTY.equals(property.getKey())) { | ||
factory.setNamespaceAware(property.getValue()); | ||
} else if (IGNORE_COMMENTS_PROPERTY.equals(property.getKey())) { | ||
factory.setIgnoringComments(property.getValue()); | ||
} else if (IGNORE_ELEMENT_CONTENT_WHITESPACE_PROPERTY.equals(property.getKey())) { | ||
factory.setIgnoringElementContentWhitespace(property.getValue()); | ||
} else { | ||
throw new IllegalArgumentException("Property not supported: " + property.getKey()); | ||
} | ||
} | ||
} | ||
|
||
if (!isXxeEnabled()) { | ||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); | ||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false); | ||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); | ||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); | ||
|
||
factory.setXIncludeAware(false); | ||
factory.setExpandEntityReferences(false); | ||
} | ||
|
||
return factory.newDocumentBuilder(); | ||
} | ||
|
||
public static XMLStreamReader createXMLStreamReader(InputStream inputStream) throws XMLStreamException { | ||
XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); | ||
|
||
if (!isXxeEnabled()) { | ||
// This disables DTDs entirely for that factory | ||
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); | ||
// disable external entities | ||
xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false); | ||
} | ||
|
||
return xmlInputFactory.createXMLStreamReader(inputStream); | ||
} | ||
|
||
public static Schema newSchema(Source schema, Map<String, Boolean> features) throws SAXException { | ||
return newSchemaFactory(features).newSchema(schema); | ||
} | ||
|
||
private static SchemaFactory newSchemaFactory(Map<String, Boolean> features) throws SAXException { | ||
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); | ||
|
||
if (features != null) { | ||
for (Map.Entry<String, Boolean> feature : features.entrySet()) { | ||
factory.setFeature(feature.getKey(), feature.getValue()); | ||
} | ||
} | ||
|
||
if (!isXxeEnabled()) { | ||
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); | ||
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); | ||
|
||
factory.setResourceResolver((type, namespaceURI, publicId, systemId, baseURI) -> { | ||
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(type) && XMLConstants.XML_NS_URI.equals(namespaceURI) && ARTEMIS_XML_SCHEMA_SID.equals(systemId)) { | ||
return newLSInput(publicId, systemId, baseURI, Thread.currentThread().getContextClassLoader().getResourceAsStream(ARTEMIS_XML_SCHEMA_URL)); | ||
} else if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(type) && ACTIVEMQ_CORE_NS.equals(namespaceURI) && ARTEMIS_CONFIGURATION_SCHEMA_SID.equals(systemId)) { | ||
return newLSInput(publicId, systemId, baseURI, Thread.currentThread().getContextClassLoader().getResourceAsStream(ARTEMIS_CONFIGURATION_SCHEMA_URL)); | ||
} else if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(type) && ACTIVEMQ_JMS_NS.equals(namespaceURI) && ARTEMIS_JMS_SCHEMA_SID.equals(systemId)) { | ||
return newLSInput(publicId, systemId, baseURI, Thread.currentThread().getContextClassLoader().getResourceAsStream(ARTEMIS_JMS_SCHEMA_URL)); | ||
} | ||
|
||
return null; | ||
}); | ||
} | ||
|
||
return factory; | ||
} | ||
|
||
public static Transformer newTransformer() throws TransformerConfigurationException { | ||
TransformerFactory transformerFactory = TransformerFactory.newInstance(); | ||
|
||
if (!isXxeEnabled()) { | ||
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); | ||
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); | ||
} | ||
|
||
return transformerFactory.newTransformer(); | ||
} | ||
|
||
public static Validator newValidator(URL schema) throws SAXException { | ||
Validator validator = newSchemaFactory(null).newSchema(schema).newValidator(); | ||
|
||
if (!isXxeEnabled()) { | ||
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); | ||
validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); | ||
} | ||
|
||
return validator; | ||
} | ||
|
||
private static LSInput newLSInput(String publicId, String systemId, String baseURI, InputStream byteStream) { | ||
return new LSInput() { | ||
@Override | ||
public Reader getCharacterStream() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public void setCharacterStream(Reader reader) { | ||
|
||
} | ||
|
||
@Override | ||
public InputStream getByteStream() { | ||
return byteStream; | ||
} | ||
|
||
@Override | ||
public void setByteStream(InputStream inputStream) { | ||
|
||
} | ||
|
||
@Override | ||
public String getStringData() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public void setStringData(String s) { | ||
|
||
} | ||
|
||
@Override | ||
public String getSystemId() { | ||
return systemId; | ||
} | ||
|
||
@Override | ||
public void setSystemId(String s) { | ||
|
||
} | ||
|
||
@Override | ||
public String getPublicId() { | ||
return publicId; | ||
} | ||
|
||
@Override | ||
public void setPublicId(String s) { | ||
|
||
} | ||
|
||
@Override | ||
public String getBaseURI() { | ||
return baseURI; | ||
} | ||
|
||
@Override | ||
public void setBaseURI(String s) { | ||
|
||
} | ||
|
||
@Override | ||
public String getEncoding() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public void setEncoding(String s) { | ||
|
||
} | ||
|
||
@Override | ||
public boolean getCertifiedText() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public void setCertifiedText(boolean b) { | ||
|
||
} | ||
}; | ||
} | ||
} |
Oops, something went wrong.