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();

Reply via email to