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