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-jxpath.git


The following commit(s) were added to refs/heads/master by this push:
     new 90244984 Harden XML parsing via commons-secure-xml (#288)
90244984 is described below

commit 9024498413b6c2f6ab8e744a5089cbe4088a8ad0
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Sun Sep 6 14:54:52 2026 +0200

    Harden XML parsing via commons-secure-xml (#288)
    
    * Harden XML parsing via commons-secure-xml
    
    Create XML parsers and transformers through
    org.apache.commons:commons-secure-xml. The secure 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.
    
    Changes:
    - Add the commons-secure-xml dependency (1.0.0-SNAPSHOT until its first
      release).
    - Route factory creation through SecureDocumentBuilderFactory in
      DOMParser and SecureTransformerFactory in XMLDocumentContainer; the
      caller-configurable factory settings (validation, namespace awareness,
      entity expansion, whitespace, comments, coalescing) keep working.
    - JDOMParser builds its SAX reader through the secure factory as well,
      by overriding SAXBuilder.createParser(); documents with internal DTD
      subsets parse as before.
    - Parsers registered through DocumentContainer.registerXMLParser remain
      under the control of their authors.
    - Run the CI and CodeQL builds with -Puse-apache-snapshots (inherited
      from the org.apache:apache parent POM) so the commons-secure-xml
      SNAPSHOT resolves; CodeQL's autobuild receives the profile through
      MAVEN_ARGS.
    
    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 +
 .../apache/commons/jxpath/XMLDocumentContainer.java   |  4 ++--
 .../java/org/apache/commons/jxpath/xml/DOMParser.java |  3 ++-
 .../org/apache/commons/jxpath/xml/JDOMParser.java     | 19 ++++++++++++++++++-
 5 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 7d5d9079..63bac126 100644
--- a/pom.xml
+++ b/pom.xml
@@ -149,6 +149,11 @@
     </dependencies>
   </dependencyManagement>
   <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-secure-xml</artifactId>
+      <version>1.0.0</version>
+    </dependency>
     <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index cc310276..32630e06 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -49,6 +49,7 @@ The <action> type attribute can be add,update,fix,remove.
     <!-- The release date is the date RC is cut -->
     <release version="1.4.1" date="YYYY-MM-DD" description="This is a 
maintenance release. Java 8 or later is required.">
       <!-- FIX -->
+      <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Create the DOM and JDOM parsers and the XML transformer through 
org.apache.commons:commons-secure-xml, so external entities and DTDs are no 
longer fetched by default.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">POM 
assembly:single does not generate binary convenience files (tar/zip).</action>
       <action type="fix" dev="ggregory" due-to="Dima1224, Gary Gregory">Make 
dynamicPropertyHandlerMap in ValueUtils thread-safe #251.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Refactor 
JXPathIntrospector internal static maps to use concurrent classes instead of 
synchronization.</action>
diff --git a/src/main/java/org/apache/commons/jxpath/XMLDocumentContainer.java 
b/src/main/java/org/apache/commons/jxpath/XMLDocumentContainer.java
index cb09f657..c8001586 100644
--- a/src/main/java/org/apache/commons/jxpath/XMLDocumentContainer.java
+++ b/src/main/java/org/apache/commons/jxpath/XMLDocumentContainer.java
@@ -22,10 +22,10 @@ import java.util.Objects;
 
 import javax.xml.transform.Source;
 import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.dom.DOMResult;
 
 import org.apache.commons.jxpath.xml.DocumentContainer;
+import org.apache.commons.xml.secure.SecureTransformerFactory;
 
 /**
  * An XML document container reads and parses XML only when it is accessed. 
JXPath traverses Containers transparently - you use the same paths to access 
objects
@@ -85,7 +85,7 @@ public class XMLDocumentContainer implements Container {
             try {
                 if (source != null) {
                     final DOMResult result = new DOMResult();
-                    final Transformer trans = 
TransformerFactory.newInstance().newTransformer();
+                    final Transformer trans = 
SecureTransformerFactory.newInstance().newTransformer();
                     trans.transform(source, result);
                     document = result.getNode();
                 } else {
diff --git a/src/main/java/org/apache/commons/jxpath/xml/DOMParser.java 
b/src/main/java/org/apache/commons/jxpath/xml/DOMParser.java
index 26e7c69d..35796d5b 100644
--- a/src/main/java/org/apache/commons/jxpath/xml/DOMParser.java
+++ b/src/main/java/org/apache/commons/jxpath/xml/DOMParser.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
 import javax.xml.parsers.DocumentBuilderFactory;
 
 import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
 
 /**
  * An implementation of the XMLParser interface that produces a DOM Document.
@@ -38,7 +39,7 @@ public class DOMParser extends XMLParser2 {
     @Override
     public Object parseXML(final InputStream stream) {
         try {
-            final DocumentBuilderFactory factory = 
DocumentBuilderFactory.newInstance();
+            final DocumentBuilderFactory factory = 
SecureDocumentBuilderFactory.newInstance();
             factory.setValidating(isValidating());
             factory.setNamespaceAware(isNamespaceAware());
             
factory.setIgnoringElementContentWhitespace(isIgnoringElementContentWhitespace());
diff --git a/src/main/java/org/apache/commons/jxpath/xml/JDOMParser.java 
b/src/main/java/org/apache/commons/jxpath/xml/JDOMParser.java
index a10122cd..bddea63a 100644
--- a/src/main/java/org/apache/commons/jxpath/xml/JDOMParser.java
+++ b/src/main/java/org/apache/commons/jxpath/xml/JDOMParser.java
@@ -19,8 +19,13 @@ package org.apache.commons.jxpath.xml;
 
 import java.io.InputStream;
 
+import javax.xml.parsers.SAXParserFactory;
+
 import org.apache.commons.jxpath.JXPathException;
+import org.apache.commons.xml.secure.SecureSAXParserFactory;
+import org.jdom.JDOMException;
 import org.jdom.input.SAXBuilder;
+import org.xml.sax.XMLReader;
 
 /**
  * An implementation of the XMLParser interface that produces a JDOM Document.
@@ -40,7 +45,19 @@ public class JDOMParser extends XMLParser2 {
             throw new JXPathException("JDOM parser configuration error. JDOM 
does not support the namespaceAware=false setting.");
         }
         try {
-            final SAXBuilder builder = new SAXBuilder();
+            // JDOM builds its reader through JAXP internally; hand it one 
from the secure factory instead.
+            final SAXBuilder builder = new SAXBuilder() {
+                @Override
+                protected XMLReader createParser() throws JDOMException {
+                    try {
+                        final SAXParserFactory factory = 
SecureSAXParserFactory.newNSInstance();
+                        factory.setValidating(isValidating());
+                        return factory.newSAXParser().getXMLReader();
+                    } catch (final Exception ex) {
+                        throw new JDOMException("Unable to create a new XML 
reader", ex);
+                    }
+                }
+            };
             builder.setExpandEntities(isExpandEntityReferences());
             
builder.setIgnoringElementContentWhitespace(isIgnoringElementContentWhitespace());
             builder.setValidation(isValidating());

Reply via email to