This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-configuration.git
The following commit(s) were added to refs/heads/master by this push:
new 3e84bcb09 Harden XML parsing via commons-xml (#647)
3e84bcb09 is described below
commit 3e84bcb091f03ba3db96379b56182b22c2436f75
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Sun Sep 6 15:29:53 2026 +0200
Harden XML parsing via commons-xml (#647)
* Harden XML parsing via commons-xml
Replace all direct JAXP factory instantiations (DocumentBuilderFactory,
SAXParserFactory, TransformerFactory) with the secure factories from
org.apache.commons:commons-xml. These factories enable
FEATURE_SECURE_PROCESSING and install a non-removable entity-resolver
floor on every parser they produce: external DTD, entity, schema and
XInclude lookups that a caller-set resolver does not resolve are
resolved to empty content instead of being fetched, and internal entity
expansion is bounded, regardless of the JAXP implementation on the
classpath.
Hardening the parsing of a configuration file is admittedly not
necessary: configuration files are normally trusted. This limits the
side-effects if a user (against advice) decides to parse untrusted
configuration files.
Changes:
- Add the commons-xml dependency (1.0.0-SNAPSHOT until its first
release).
- Route factory creation through SecureDocumentBuilderFactory,
SecureSAXParserFactory and SecureTransformerFactory in
XMLConfiguration, XMLDocumentHelper, XMLPropertiesConfiguration and
XMLPropertyListConfiguration, plus the affected tests.
- No explicit hardening of the source passed to
XMLDocumentHelper.transform is needed: transformers created by
SecureTransformerFactory rewrite their sources on every transform
call.
- XMLConfiguration keeps its DefaultEntityResolver contract (return
null for unknown entities): a null return no longer lets the parser
fetch the external resource, because the resolver floor resolves it
to empty content instead.
- Keep resolver-based schema validation (for example via
CatalogResolver) working under secure processing: disable
EntityResolver2 handling
(http://xml.org/sax/features/use-entity-resolver2), because the JDK
does not mark schema documents supplied by an EntityResolver2 as
resolver-created and its accessExternalSchema check then refuses
them, and lift the accessExternalSchema restriction, because Java 8
applies it even to sources supplied by a plain resolver. Lifting the
restriction does not reopen external fetches: the secure factory's
resolver floor still resolves every unresolved lookup to empty
content instead of fetching it.
- Run the CI and CodeQL builds with -Puse-apache-snapshots (inherited
from the org.apache:apache parent POM) so the commons-xml SNAPSHOT
resolves; CodeQL's autobuild receives the profile through MAVEN_ARGS.
Assisted-By: Claude Opus 4.8 (1M context) <[email protected]>
Assisted-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn
* Resolve schema locally in TestMultiFileConfigurationBuilder
The 2001 test configuration references its schema through an absolute
https URI (xsi:noNamespaceSchemaLocation). The hardened parser does not
fetch external resources, so testSchemaValidationError no longer reached
the intended schema validation error; the entity resolver refused the
remote fetch first.
Register the local testMultiConfiguration.xsd for that system URI via an
XML catalog (CatalogResolver pointing at the existing catalog.xml, which
already rewrites https://commons.apache.org/ to the local test
resources) and set it as the builder's entity resolver. The schema now
loads locally, so the expected SAXParseException validation error is
raised again.
Assisted-By: Claude Opus 4.8 (1M context) <[email protected]>
* Simplify the secure schema validation workaround
Disabling EntityResolver2 handling is unnecessary: the parser does use
a schema document supplied by an EntityResolver2 once the
accessExternalSchema check no longer refuses it, so lifting that
restriction alone keeps resolver-based schema validation working on
every JDK version.
Also inline XMLDocumentHelper.createTransformerFactory into its only
caller and drop its test.
Assisted-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn
* Remove unused import in TestXMLDocumentHelper
Assisted-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NS6CoaDG2mfSNpy4Ukhvrn
* Adopt the commons-secure-xml artifact name
The org.apache.commons:commons-xml artifact was renamed to
org.apache.commons:commons-secure-xml and its Java package moved from
org.apache.commons.xml to org.apache.commons.xml.secure. Update the
dependency and imports accordingly, and record the hardening in
changes.xml.
Assisted-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MHgnMnGWHQoH2zD2jFdoMT
* Use the Commons Secure XML 1.0.0 release candidate
Bump org.apache.commons:commons-secure-xml from 1.0.0-SNAPSHOT to 1.0.0
and add the temporary staging repository
https://repository.apache.org/content/repositories/orgapachecommons-1962/
after Central, so the vote gets downstream CI results. Drop the
-Puse-apache-snapshots profile from the CI workflows, which the release
version no longer needs. Remove the staging repository once 1.0.0 is
released.
Assisted-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0167e29ScPEdfzJnEFm95imK
* Bump Apache Commons Secure XML from 1.0.0-SNAPSHOT to 1.0.0
---------
Co-authored-by: Gary Gregory <[email protected]>
---
pom.xml | 5 +++++
src/changes/changes.xml | 1 +
.../commons/configuration2/XMLConfiguration.java | 18 +++++++++++++++-
.../commons/configuration2/XMLDocumentHelper.java | 24 ++++------------------
.../configuration2/XMLPropertiesConfiguration.java | 3 ++-
.../plist/XMLPropertyListConfiguration.java | 3 ++-
.../TestBaseConfigurationXMLReader.java | 4 ++--
.../TestHierarchicalConfigurationXMLReader.java | 4 ++--
.../configuration2/TestXMLConfiguration.java | 10 +++++----
.../configuration2/TestXMLDocumentHelper.java | 11 ++--------
.../TestXMLPropertiesConfiguration.java | 8 +++++---
.../TestMultiFileConfigurationBuilder.java | 9 +++++++-
12 files changed, 56 insertions(+), 44 deletions(-)
diff --git a/pom.xml b/pom.xml
index 190a769ff..21e88487e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,6 +96,11 @@
</site>
</distributionManagement>
<dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-secure-xml</artifactId>
+ <version>1.0.0</version>
+ </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 55dd9273e..bbfa19cf8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -32,6 +32,7 @@
<action type="fix" dev="ggregory" due-to="Gary Gregory">Fix SpotBugs
USO_UNSAFE_METHOD_SYNCHRONIZATION in MultiFileConfigurationBuilder.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">Fix SpotBugs
USO_UNSAFE_METHOD_SYNCHRONIZATION in
ReloadingCombinedConfigurationBuilder.</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory">Fix SpotBugs
USO_UNSAFE_ACCESSIBLE_OBJECT_SYNCHRONIZATION in
XMLPropertyListConfiguration.</action>
+ <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary
Gregory">Create XML parsers through org.apache.commons:commons-secure-xml, so
external entities and DTDs are no longer fetched by default.</action>
<!-- ADD -->
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump
org.apache.commons:commons-parent from 100 to 104.</action>
diff --git
a/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
b/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
index fb97d08f3..9a335e1d5 100644
--- a/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
+++ b/src/main/java/org/apache/commons/configuration2/XMLConfiguration.java
@@ -31,6 +31,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -53,6 +54,7 @@ import org.apache.commons.configuration2.tree.NodeTreeWalker;
import org.apache.commons.configuration2.tree.ReferenceNodeHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.MutableObject;
+import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
@@ -693,12 +695,26 @@ public class XMLConfiguration extends
BaseHierarchicalConfiguration implements F
if (getDocumentBuilder() != null) {
return getDocumentBuilder();
}
- final DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
+ final DocumentBuilderFactory factory =
SecureDocumentBuilderFactory.newInstance();
if (isValidating()) {
factory.setValidating(true);
if (isSchemaValidation()) {
factory.setNamespaceAware(true);
factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
+ try {
+ // Due to a bug, the JDK fails to mark schema documents
supplied by an entity resolver as resolver-created,
+ // so the accessExternalSchema check is applied to them
and denies access:
+ //
+ // - Old JDK 8 versions never mark them.
+ // - Newer JDK 8 and later versions only fail to mark
documents supplied by an EntityResolver2.
+ //
+ // Allowing all protocols only stops that check from
refusing the documents returned by the resolver:
+ // the parser never retrieves a schema itself, because the
Commons XML ignore-all resolver floor
+ // resolves every lookup the caller-supplied resolver
leaves unresolved to empty content.
+ factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA,
"all");
+ } catch (final IllegalArgumentException ignored) {
+ // Xerces-specific settings: other parsers keep their own
configuration.
+ }
}
}
diff --git
a/src/main/java/org/apache/commons/configuration2/XMLDocumentHelper.java
b/src/main/java/org/apache/commons/configuration2/XMLDocumentHelper.java
index 3c570e086..3a4612e93 100644
--- a/src/main/java/org/apache/commons/configuration2/XMLDocumentHelper.java
+++ b/src/main/java/org/apache/commons/configuration2/XMLDocumentHelper.java
@@ -33,6 +33,8 @@ import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -92,15 +94,6 @@ final class XMLDocumentHelper {
}
}
- /**
- * Creates a new {@code DocumentBuilderFactory} instance.
- *
- * @return The new factory object
- */
- private static DocumentBuilderFactory createDocumentBuilderFactory() {
- return DocumentBuilderFactory.newInstance();
- }
-
/**
* Creates the element mapping for the specified documents. For each node
in the source document an entry is created
* pointing to the corresponding node in the destination object.
@@ -139,7 +132,7 @@ final class XMLDocumentHelper {
* @throws ConfigurationException if the {@code Transformer} could not be
created
*/
public static Transformer createTransformer() throws
ConfigurationException {
- return createTransformer(createTransformerFactory());
+ return createTransformer(SecureTransformerFactory.newInstance());
}
/**
@@ -157,15 +150,6 @@ final class XMLDocumentHelper {
}
}
- /**
- * Creates a new {@code TransformerFactory}.
- *
- * @return The {@code TransformerFactory}
- */
- static TransformerFactory createTransformerFactory() {
- return TransformerFactory.newInstance();
- }
-
/**
* Creates an empty element mapping.
*
@@ -184,7 +168,7 @@ final class XMLDocumentHelper {
* @throws ConfigurationException if an error occurs when creating the
document
*/
public static XMLDocumentHelper forNewDocument(final String
rootElementName) throws ConfigurationException {
- final Document doc =
createDocumentBuilder(createDocumentBuilderFactory()).newDocument();
+ final Document doc =
createDocumentBuilder(SecureDocumentBuilderFactory.newInstance()).newDocument();
final Element rootElem = doc.createElement(rootElementName);
doc.appendChild(rootElem);
return new XMLDocumentHelper(doc, emptyElementMapping(), null, null);
diff --git
a/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
b/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
index d5b217e63..9ac5f2eed 100644
---
a/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
+++
b/src/main/java/org/apache/commons/configuration2/XMLPropertiesConfiguration.java
@@ -32,6 +32,7 @@ import
org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileLocator;
import org.apache.commons.configuration2.io.FileLocatorAware;
import org.apache.commons.text.StringEscapeUtils;
+import org.apache.commons.xml.secure.SecureSAXParserFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -221,7 +222,7 @@ public class XMLPropertiesConfiguration extends
BaseConfiguration implements Fil
@Override
public void read(final Reader in) throws ConfigurationException {
- final SAXParserFactory factory = SAXParserFactory.newInstance();
+ final SAXParserFactory factory = SecureSAXParserFactory.newInstance();
factory.setNamespaceAware(false);
factory.setValidating(true);
try {
diff --git
a/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
b/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
index 04b415cca..0d7b4b268 100644
---
a/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
+++
b/src/main/java/org/apache/commons/configuration2/plist/XMLPropertyListConfiguration.java
@@ -55,6 +55,7 @@ import org.apache.commons.configuration2.tree.ImmutableNode;
import org.apache.commons.configuration2.tree.InMemoryNodeModel;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
+import org.apache.commons.xml.secure.SecureSAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
@@ -661,7 +662,7 @@ public class XMLPropertyListConfiguration extends
BaseHierarchicalConfiguration
// parse the file
final XMLPropertyListHandler handler = new XMLPropertyListHandler();
try {
- final SAXParserFactory factory = SAXParserFactory.newInstance();
+ final SAXParserFactory factory =
SecureSAXParserFactory.newInstance();
factory.setValidating(true);
final XMLReader xmlReader = factory.newSAXParser().getXMLReader();
xmlReader.setEntityResolver(resolver);
diff --git
a/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
b/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
index 6ce26a9a9..73a315e7f 100644
---
a/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
+++
b/src/test/java/org/apache/commons/configuration2/TestBaseConfigurationXMLReader.java
@@ -27,11 +27,11 @@ import java.util.Arrays;
import java.util.Iterator;
import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
@@ -80,7 +80,7 @@ public class TestBaseConfigurationXMLReader {
private void checkDocument(final BaseConfigurationXMLReader creader, final
String rootName) throws Exception {
final SAXSource source = new SAXSource(creader, new InputSource());
final DOMResult result = new DOMResult();
- final Transformer trans =
TransformerFactory.newInstance().newTransformer();
+ final Transformer trans =
SecureTransformerFactory.newInstance().newTransformer();
trans.transform(source, result);
final Node root = ((Document) result.getNode()).getDocumentElement();
final JXPathContext ctx = JXPathContext.newContext(root);
diff --git
a/src/test/java/org/apache/commons/configuration2/TestHierarchicalConfigurationXMLReader.java
b/src/test/java/org/apache/commons/configuration2/TestHierarchicalConfigurationXMLReader.java
index ad801f7a8..fa50fd8fc 100644
---
a/src/test/java/org/apache/commons/configuration2/TestHierarchicalConfigurationXMLReader.java
+++
b/src/test/java/org/apache/commons/configuration2/TestHierarchicalConfigurationXMLReader.java
@@ -20,13 +20,13 @@ package org.apache.commons.configuration2;
import static org.junit.jupiter.api.Assertions.assertEquals;
import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXSource;
import org.apache.commons.configuration2.io.FileHandler;
import org.apache.commons.configuration2.tree.ImmutableNode;
import org.apache.commons.jxpath.JXPathContext;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
@@ -53,7 +53,7 @@ public class TestHierarchicalConfigurationXMLReader {
void testParse() throws Exception {
final SAXSource source = new SAXSource(parser, new InputSource());
final DOMResult result = new DOMResult();
- final Transformer trans =
TransformerFactory.newInstance().newTransformer();
+ final Transformer trans =
SecureTransformerFactory.newInstance().newTransformer();
trans.transform(source, result);
final Node root = ((Document) result.getNode()).getDocumentElement();
final JXPathContext ctx = JXPathContext.newContext(root);
diff --git
a/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
b/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
index 279216662..8d6cda096 100644
--- a/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
+++ b/src/test/java/org/apache/commons/configuration2/TestXMLConfiguration.java
@@ -64,6 +64,8 @@ import org.apache.commons.configuration2.tree.ImmutableNode;
import org.apache.commons.configuration2.tree.NodeStructureHelper;
import org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
@@ -155,7 +157,7 @@ public class TestXMLConfiguration {
final Source source = new DOMSource(node);
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final Result result = new StreamResult(bos);
- final TransformerFactory factory = TransformerFactory.newInstance();
+ final TransformerFactory factory =
SecureTransformerFactory.newInstance();
factory.newTransformer().transform(source, result);
// 4. Return the resulting byte array
return bos.toByteArray();
@@ -184,7 +186,7 @@ public class TestXMLConfiguration {
private Node buildDomNodeFixture() throws SAXException, IOException,
ParserConfigurationException {
final String content = "<configuration><test
attr=\"x\">1</test></configuration>";
- final Node document =
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new
ByteArrayInputStream(content.getBytes()));
+ final Node document =
SecureDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new
ByteArrayInputStream(content.getBytes()));
final Node node = document.getFirstChild().getFirstChild(); // <test>
assertEquals("test", node.getNodeName()); // sanity check
return node;
@@ -239,7 +241,7 @@ public class TestXMLConfiguration {
* @throws ParserConfigurationException if an error occurs
*/
private DocumentBuilder createValidatingDocBuilder() throws
ParserConfigurationException {
- final DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
+ final DocumentBuilderFactory factory =
SecureDocumentBuilderFactory.newInstance();
factory.setValidating(true);
final DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new DefaultHandler() {
@@ -252,7 +254,7 @@ public class TestXMLConfiguration {
}
private Document parseXml(final String xml) throws SAXException,
IOException, ParserConfigurationException {
- return
DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
+ return
SecureDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new
ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)));
}
/**
diff --git
a/src/test/java/org/apache/commons/configuration2/TestXMLDocumentHelper.java
b/src/test/java/org/apache/commons/configuration2/TestXMLDocumentHelper.java
index 35cff0a22..e2792722c 100644
--- a/src/test/java/org/apache/commons/configuration2/TestXMLDocumentHelper.java
+++ b/src/test/java/org/apache/commons/configuration2/TestXMLDocumentHelper.java
@@ -45,6 +45,7 @@ import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@@ -134,7 +135,7 @@ public class TestXMLDocumentHelper {
* @return The parsed document
*/
private static Document loadDocument(final String name) throws
IOException, SAXException, ParserConfigurationException {
- final DocumentBuilder builder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ final DocumentBuilder builder =
SecureDocumentBuilderFactory.newInstance().newDocumentBuilder();
return builder.parse(ConfigurationAssert.getTestFile(name));
}
@@ -199,14 +200,6 @@ public class TestXMLDocumentHelper {
assertEquals(pcex, cex.getCause());
}
- /**
- * Tests whether a correct transformer factory can be created.
- */
- @Test
- void testCreateTransformerFactory() {
- assertNotNull(XMLDocumentHelper.createTransformerFactory());
- }
-
/**
* Tests whether exceptions while creating transformers are correctly
handled.
*/
diff --git
a/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
b/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
index b7e83751d..62bbff031 100644
---
a/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
+++
b/src/test/java/org/apache/commons/configuration2/TestXMLPropertiesConfiguration.java
@@ -35,6 +35,8 @@ import javax.xml.transform.stream.StreamResult;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.io.FileHandler;
+import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.w3c.dom.Document;
@@ -72,7 +74,7 @@ public class TestXMLPropertiesConfiguration {
assertThrows(NullPointerException.class, () -> new
XMLPropertiesConfiguration(null));
// Normal case
final URL location =
ConfigurationAssert.getTestURL(TEST_PROPERTIES_FILE);
- final DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
+ final DocumentBuilderFactory dbFactory =
SecureDocumentBuilderFactory.newInstance();
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
dBuilder.setEntityResolver((publicId, systemId) -> new
InputSource(getClass().getClassLoader().getResourceAsStream("properties.dtd")));
final File file = new File(location.toURI());
@@ -101,11 +103,11 @@ public class TestXMLPropertiesConfiguration {
final File saveFile = newFile("test2.properties.xml", tempFolder);
// save as DOM into saveFile
- final DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
+ final DocumentBuilderFactory dbFactory =
SecureDocumentBuilderFactory.newInstance();
final DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
final Document document = dBuilder.newDocument();
conf.save(document, document);
- final TransformerFactory tFactory = TransformerFactory.newInstance();
+ final TransformerFactory tFactory =
SecureTransformerFactory.newInstance();
final Transformer transformer = tFactory.newTransformer();
final DOMSource source = new DOMSource(document);
final Result result = new StreamResult(saveFile);
diff --git
a/src/test/java/org/apache/commons/configuration2/builder/combined/TestMultiFileConfigurationBuilder.java
b/src/test/java/org/apache/commons/configuration2/builder/combined/TestMultiFileConfigurationBuilder.java
index d1e51f0f8..e43d6a964 100644
---
a/src/test/java/org/apache/commons/configuration2/builder/combined/TestMultiFileConfigurationBuilder.java
+++
b/src/test/java/org/apache/commons/configuration2/builder/combined/TestMultiFileConfigurationBuilder.java
@@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import org.apache.commons.configuration2.ConfigurationAssert;
import org.apache.commons.configuration2.ConfigurationLookup;
import org.apache.commons.configuration2.DynamicCombinedConfiguration;
import org.apache.commons.configuration2.HierarchicalConfiguration;
@@ -49,6 +50,7 @@ import
org.apache.commons.configuration2.event.EventListenerTestImpl;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.interpol.ConfigurationInterpolator;
import org.apache.commons.configuration2.interpol.DefaultLookups;
+import org.apache.commons.configuration2.resolver.CatalogResolver;
import org.apache.commons.configuration2.tree.ExpressionEngine;
import org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine;
import org.junit.jupiter.api.Test;
@@ -334,8 +336,13 @@ public class TestMultiFileConfigurationBuilder extends
AbstractMultiFileConfigur
*/
@Test
void testSchemaValidationError() {
+ // The testMultiConfiguration_2001.xml configuration references its
schema through an absolute https URI.
+ // The hardened parser does not fetch external resources,
+ // so register the local testMultiConfiguration.xsd for that system
URI via an XML catalog.
+ final CatalogResolver resolver = new CatalogResolver();
+
resolver.setCatalogFiles(ConfigurationAssert.getTestFile("catalog.xml").getAbsolutePath());
final MultiFileConfigurationBuilder<XMLConfiguration> builder =
createTestBuilder(
- new
XMLBuilderParametersImpl().setValidating(true).setSchemaValidation(true));
+ new
XMLBuilderParametersImpl().setValidating(true).setSchemaValidation(true).setEntityResolver(resolver));
switchToConfig("2001");
final ConfigurationException ex =
assertThrows(ConfigurationException.class, builder::getConfiguration);
Throwable cause = ex.getCause();