ARTEMIS-4077 Add an option to disable XML external entity processing

This commit is contained in:
Domenico Francesco Bruscino 2022-10-26 19:26:03 +02:00 committed by Justin Bertram
parent 4f79eb42f5
commit 3a13a7850c
19 changed files with 569 additions and 65 deletions

View File

@ -16,13 +16,9 @@
*/
package org.apache.activemq.artemis.cli.commands.tools.xml;
import javax.xml.XMLConstants;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stax.StAXSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;
import java.io.FileInputStream;
@ -63,6 +59,7 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.utils.ClassloadingUtil;
import org.apache.activemq.artemis.utils.ListUtil;
import org.apache.activemq.artemis.utils.XmlProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
@ -175,7 +172,7 @@ public final class XmlDataImporter extends ActionAbstract {
public void process(InputStream inputStream,
ClientSession session,
ClientSession managementSession) throws Exception {
reader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
reader = XmlProvider.createXMLStreamReader(inputStream);
messageReader = new XMLMessageImporter(reader, session);
messageReader.setOldPrefixTranslation(oldPrefixTranslation);
@ -219,11 +216,8 @@ public final class XmlDataImporter extends ActionAbstract {
}
public void validate(InputStream inputStream) throws Exception {
XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(XmlDataImporter.findResource("schema/artemis-import-export.xsd"));
Validator validator = schema.newValidator();
XMLStreamReader reader = XmlProvider.createXMLStreamReader(inputStream);
Validator validator = XmlProvider.newValidator(XmlDataImporter.findResource("schema/artemis-import-export.xsd"));
validator.validate(new StAXSource(reader));
reader.close();
}

View File

@ -18,7 +18,6 @@ package org.apache.activemq.artemis.cli.factory.serialize;
import javax.jms.Message;
import javax.jms.Session;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
@ -36,6 +35,7 @@ import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataConstants;
import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataExporter;
import org.apache.activemq.artemis.jms.client.ActiveMQMessage;
import org.apache.activemq.artemis.jms.client.ActiveMQSession;
import org.apache.activemq.artemis.utils.XmlProvider;
public class XMLMessageSerializer implements MessageSerializer {
@ -87,7 +87,7 @@ public class XMLMessageSerializer implements MessageSerializer {
@Override
public void setInput(InputStream inputStream, Session session) throws Exception {
XMLStreamReader streamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
XMLStreamReader streamReader = XmlProvider.createXMLStreamReader(inputStream);
this.clientSession = ((ActiveMQSession) session).getCoreSession();
this.reader = new XMLMessageImporter(streamReader, clientSession);
}

View File

@ -29,10 +29,8 @@ import org.apache.activemq.artemis.api.core.QueueConfiguration;
import org.apache.activemq.artemis.json.JsonArray;
import org.apache.activemq.artemis.json.JsonObject;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.BufferedReader;
@ -94,6 +92,7 @@ import org.apache.activemq.artemis.utils.RandomUtil;
import org.apache.activemq.artemis.utils.SensitiveDataCodec;
import org.apache.activemq.artemis.utils.StringUtil;
import org.apache.activemq.artemis.utils.Wait;
import org.apache.activemq.artemis.utils.XmlProvider;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Configurations;
@ -368,8 +367,7 @@ public class ArtemisTest extends CliTestBase {
originalBootstrapFile.delete();
// write the modified config into the bootstrap.xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
Transformer transformer = XmlProvider.newTransformer();
DOMSource source = new DOMSource(config);
StreamResult streamResult = new StreamResult(originalBootstrapFile);
transformer.transform(source, streamResult);
@ -2195,8 +2193,7 @@ public class ArtemisTest extends CliTestBase {
}
private static Document parseXml(File xmlFile) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder domBuilder = domFactory.newDocumentBuilder();
DocumentBuilder domBuilder = XmlProvider.newDocumentBuilder();
return domBuilder.parse(xmlFile);
}

View File

@ -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) {
}
};
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.parsers.DocumentBuilder;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Validator;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@RunWith(Parameterized.class)
public class XmlProviderTest {
@Parameterized.Parameters(name = "xxeEnabled={0}")
public static Collection getParameters() {
return Arrays.asList(new Boolean[]{true, false});
}
public XmlProviderTest(boolean xxeEnabled) {
XmlProvider.setXxeEnabled(xxeEnabled);
}
@Test
public void testDocument() throws Exception {
DocumentBuilder documentBuilder = XmlProvider.newDocumentBuilder();
Document document = documentBuilder.parse(new File(getClass().getResource("/document.xml").toURI()));
Element documentElement = document.getDocumentElement();
Assert.assertEquals("t:book", documentElement.getTagName());
Assert.assertEquals(1, documentElement.getElementsByTagName("title").getLength());
}
@Test
public void testDocumentWithXmlInclude() throws Exception {
Map<String, Boolean> properties = new HashMap<>();
properties.put(XmlProvider.XINCLUDE_AWARE_PROPERTY, true);
properties.put(XmlProvider.NAMESPACE_AWARE_PROPERTY, true);
DocumentBuilder documentBuilder = XmlProvider.newDocumentBuilder(null, properties);
Document document = documentBuilder.parse(new File(XmlProviderTest.class.getResource("/document-with-xinclude.xml").toURI()));
Element documentElement = document.getDocumentElement();
Assert.assertEquals("t:book", documentElement.getTagName());
if (XmlProvider.isXxeEnabled()) {
Assert.assertEquals(1, documentElement.getElementsByTagName("title").getLength());
} else {
Assert.assertEquals(0, documentElement.getElementsByTagName("title").getLength());
}
}
@Test
public void testSchema() throws Exception {
StreamSource streamSource = new StreamSource(XmlProviderTest.class.getResourceAsStream("/schema.xsd"));
XmlProvider.newSchema(streamSource, null);
}
@Test
public void testSchemaWithImport() {
StreamSource streamSource = new StreamSource(XmlProviderTest.class.getResourceAsStream("/schema-with-import.xsd"));
Exception newSchemaException = null;
try {
XmlProvider.newSchema(streamSource, null);
} catch (Exception e) {
newSchemaException = e;
}
if (XmlProvider.isXxeEnabled()) {
Assert.assertNull(newSchemaException);
} else {
Assert.assertNotNull(newSchemaException);
}
}
@Test
public void testValidator() throws Exception {
Map<String, Boolean> properties = new HashMap<>();
properties.put(XmlProvider.NAMESPACE_AWARE_PROPERTY, true);
DocumentBuilder documentBuilder = XmlProvider.newDocumentBuilder(null, properties);
Document document = documentBuilder.parse(new File(getClass().getResource("/document.xml").toURI()));
Element documentElement = document.getDocumentElement();
Validator validator = XmlProvider.newValidator(XmlProviderTest.class.getResource("/schema.xsd"));
validator.validate(new DOMSource(documentElement));
}
@Test
public void testValidatorWithImport() throws Exception {
Map<String, Boolean> properties = new HashMap<>();
properties.put(XmlProvider.NAMESPACE_AWARE_PROPERTY, true);
DocumentBuilder documentBuilder = XmlProvider.newDocumentBuilder(null, properties);
Document document = documentBuilder.parse(new File(getClass().getResource("/document.xml").toURI()));
Element documentElement = document.getDocumentElement();
Exception validateException = null;
try {
Validator validator = XmlProvider.newValidator(XmlProviderTest.class.getResource("/schema-with-import.xsd"));
validator.validate(new DOMSource(documentElement));
} catch (Exception e) {
validateException = e;
}
if (XmlProvider.isXxeEnabled()) {
Assert.assertNull(validateException);
} else {
Assert.assertNotNull(validateException);
}
}
}

View File

@ -0,0 +1,17 @@
<!--
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.
-->
<title>document</title>

View File

@ -0,0 +1,19 @@
<!--
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.
-->
<t:book xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:t="urn:test">
<xi:include href="document-with-xinclude-title.xml"/>
</t:book>

View File

@ -0,0 +1,19 @@
<!--
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.
-->
<t:book xmlns:t="urn:test">
<title>test</title>
</t:book>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="urn:test" schemaLocation="schema.xsd"/>
</xs:schema>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<xs:schema targetNamespace="urn:test" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="book">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -16,12 +16,8 @@
*/
package org.apache.activemq.artemis.utils;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -31,6 +27,7 @@ import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.activemq.artemis.core.client.ActiveMQClientLogger;
@ -38,6 +35,8 @@ import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
@ -85,10 +84,10 @@ public final class XMLUtil {
}
public static Element readerToElement(final Reader r) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setXIncludeAware(true);
DocumentBuilder parser = factory.newDocumentBuilder();
Map<String, Boolean> properties = new HashMap<>();
properties.put(XmlProvider.XINCLUDE_AWARE_PROPERTY, true);
properties.put(XmlProvider.NAMESPACE_AWARE_PROPERTY, true);
DocumentBuilder parser = XmlProvider.newDocumentBuilder(null, properties);
Document doc = replaceSystemPropsInXml(parser.parse(new InputSource(new StringReader(replaceSystemPropsInString(readerToString(r))))));
return doc.getDocumentElement();
}
@ -366,10 +365,7 @@ public final class XMLUtil {
}
public static void validate(final Node node, final String schemaFile) throws Exception {
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new URL(findResource(schemaFile).toURI().toASCIIString()));
Validator validator = schema.newValidator();
Validator validator = XmlProvider.newValidator(new URL(findResource(schemaFile).toURI().toASCIIString()));
// validate the DOM tree
try {

View File

@ -16,23 +16,23 @@
*/
package org.apache.activemq.artemis.dto;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.Collections;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.activemq.artemis.utils.XmlProvider;
public class XmlUtil {
/**
@ -73,8 +73,6 @@ public class XmlUtil {
}
private static final XMLInputFactory factory = XMLInputFactory.newInstance();
public static <T> T decode(Class<T> clazz, File configuration) throws Exception {
return decode(clazz, configuration, null, null, null);
}
@ -90,11 +88,10 @@ public class XmlUtil {
JAXBContext jaxbContext = JAXBContext.newInstance("org.apache.activemq.artemis.dto");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
sf.setFeature("http://apache.org/xml/features/validation/schema-full-checking", false);
InputStream xsdStream = XmlUtil.class.getClassLoader().getResourceAsStream("org.apache.activemq/dto/activemq.xsd");
StreamSource xsdSource = new StreamSource(xsdStream);
Schema schema = sf.newSchema(xsdSource);
Schema schema = XmlProvider.newSchema(xsdSource, Collections.singletonMap(
"http://apache.org/xml/features/validation/schema-full-checking", false));
unmarshaller.setSchema(schema);
Properties props = new Properties(System.getProperties());
@ -110,7 +107,7 @@ public class XmlUtil {
props.put("artemis.URI.instance", artemisURIInstance.toString());
}
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream(configuration));
XMLStreamReader reader = XmlProvider.createXMLStreamReader(new FileInputStream(configuration));
reader = new PropertiesFilter(reader, props);

View File

@ -17,11 +17,13 @@
package org.apache.activemq.artemis.selector.filter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.activemq.artemis.utils.XmlProvider;
/**
* Used to evaluate an XPath Expression in a JMS selector.
*/
@ -36,19 +38,14 @@ public final class XPathExpression implements BooleanExpression {
public static final String DOCUMENT_BUILDER_FACTORY_FEATURE_PREFIX = "org.apache.activemq.documentBuilderFactory.feature:";
static {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
try {
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/disallow-doctype-decl", true);
// setup features from system properties (if any)
setupFeatures(factory);
builder = factory.newDocumentBuilder();
// get features from system properties (if any)
Map<String, Boolean> features = getFeatures();
Map<String, Boolean> properties = new HashMap<>();
properties.put(XmlProvider.NAMESPACE_AWARE_PROPERTY, true);
properties.put(XmlProvider.IGNORE_COMMENTS_PROPERTY, true);
properties.put(XmlProvider.IGNORE_ELEMENT_CONTENT_WHITESPACE_PROPERTY, true);
builder = XmlProvider.newDocumentBuilder(features, properties);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
@ -97,14 +94,16 @@ public final class XPathExpression implements BooleanExpression {
return object == Boolean.TRUE;
}
protected static void setupFeatures(DocumentBuilderFactory factory) throws ParserConfigurationException {
protected static Map<String, Boolean> getFeatures() throws ParserConfigurationException {
Map<String, Boolean> features = new HashMap<>();
Properties properties = System.getProperties();
for (Map.Entry<Object, Object> prop : properties.entrySet()) {
String key = (String) prop.getKey();
if (key.startsWith(DOCUMENT_BUILDER_FACTORY_FEATURE_PREFIX)) {
Boolean value = Boolean.valueOf((String)prop.getValue());
factory.setFeature(key.substring(DOCUMENT_BUILDER_FACTORY_FEATURE_PREFIX.length()), value);
features.put(key.substring(DOCUMENT_BUILDER_FACTORY_FEATURE_PREFIX.length()), value);
}
}
return features;
}
}

View File

@ -16,10 +16,7 @@
*/
package org.apache.activemq.artemis.core.deployers.impl;
import javax.xml.XMLConstants;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.InputStream;
import java.security.AccessController;
@ -110,6 +107,7 @@ import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
import org.apache.activemq.artemis.utils.XMLConfigurationUtil;
import org.apache.activemq.artemis.utils.XMLUtil;
import org.apache.activemq.artemis.utils.XmlProvider;
import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -354,9 +352,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
public Configuration parseMainConfig(final InputStream input) throws Exception {
Element e = XMLUtil.streamToElement(input);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(XMLUtil.findResource("schema/artemis-server.xsd"));
Validator validator = schema.newValidator();
Validator validator = XmlProvider.newValidator(XMLUtil.findResource("schema/artemis-server.xsd"));
try {
validator.validate(new DOMSource(e));
} catch (Exception ex) {

View File

@ -22,6 +22,8 @@ import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -72,12 +74,16 @@ import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy;
import org.apache.activemq.artemis.core.settings.impl.SlowConsumerThresholdMeasurementUnit;
import org.apache.activemq.artemis.logs.AssertionLoggerHandler;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.apache.activemq.artemis.utils.XmlProvider;
import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class FileConfigurationTest extends ConfigurationImplTest {
@BeforeClass
@ -96,6 +102,15 @@ public class FileConfigurationTest extends ConfigurationImplTest {
System.clearProperty("ninetyTwoProp");
}
@Parameterized.Parameters(name = "xxeEnabled={0}")
public static Collection getParameters() {
return Arrays.asList(new Boolean[] {true, false});
}
public FileConfigurationTest(boolean xxeEnabled) {
XmlProvider.setXxeEnabled(xxeEnabled);
}
protected String getConfigurationName() {
return "ConfigurationTest-full-config.xml";
}

View File

@ -17,10 +17,16 @@
package org.apache.activemq.artemis.core.config.impl;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
public class FileXIncludeConfigurationTest extends FileConfigurationTest {
public FileXIncludeConfigurationTest(boolean xxeEnabled) {
super(xxeEnabled);
Assume.assumeTrue(xxeEnabled);
}
@BeforeClass
public static void setupProperties() {
System.setProperty("xincludePath", "./src/test/resources");

View File

@ -46,6 +46,11 @@ For further information on XInclude see:
[https://www.w3.org/TR/xinclude/](https://www.w3.org/TR/xinclude/)
To disable XML external entity processing use the system property `artemis.disableXxe`, e.g.:
```
-Dartemis.disableXxe=true
```
##### Reloading modular configuration files
Certain changes in `broker.xml` can be picked up at runtime as discussed in the [Configuration Reload](config-reload.md)

View File

@ -16,7 +16,6 @@
*/
package org.apache.activemq.artemis.tests.integration.amqp;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
@ -24,6 +23,7 @@ import java.io.ByteArrayOutputStream;
import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataExporter;
import org.apache.activemq.artemis.protocol.amqp.converter.AMQPMessageSupport;
import org.apache.activemq.artemis.utils.XmlProvider;
import org.apache.activemq.transport.amqp.client.AmqpClient;
import org.apache.activemq.transport.amqp.client.AmqpConnection;
import org.apache.activemq.transport.amqp.client.AmqpMessage;
@ -59,7 +59,7 @@ public class XmlExportTest extends AmqpClientTestSupport {
server.getConfiguration().getPagingDirectory(),
server.getConfiguration().getLargeMessagesDirectory());
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().
Document document = XmlProvider.newDocumentBuilder().
parse(new ByteArrayInputStream(xmlOutputStream.toByteArray()));
Assert.assertNotNull(XPathFactory.newInstance().newXPath().

View File

@ -17,7 +17,6 @@
package org.apache.activemq.artemis.tests.unit.ra;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
@ -26,6 +25,7 @@ import java.util.Map;
import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.utils.XmlProvider;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -443,8 +443,7 @@ public class ActiveMQResourceAdapterConfigTest extends ActiveMQTestBase {
methodList.put(method.getName(), method);
}
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
DocumentBuilder db = XmlProvider.newDocumentBuilder();
InputStream io = new ByteArrayInputStream(rootConfig.getBytes());
Document dom = db.parse(new InputSource(io));