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


The following commit(s) were added to refs/heads/master by this push:
     new f29f66e1 Bump dom4j to 2.1.5 and keep SAXReader from bypassing Commons 
Secure XML (#109)
f29f66e1 is described below

commit f29f66e1ad66cda76a245fb4c129c4329de6ba81
Author: Piotr P. Karwasz <[email protected]>
AuthorDate: Fri Sep 18 00:38:30 2026 +0200

    Bump dom4j to 2.1.5 and keep SAXReader from bypassing Commons Secure XML 
(#109)
    
    * Bump dom4j:dom4j from 1.6.1 to org.dom4j:dom4j 2.1.5
    
    2.1.5 is the last dom4j release that runs on Java 8; 2.2.0 requires
    Java 11.
    
    dom4j 2.x drops the untyped `Stylesheet.run(Object)` and
    `applyTemplates(Object, ...)` overloads, so the JSL tag library now
    dispatches on the runtime type of its XPath context itself.
    
    The xmlunit and jaxme modules used the released commons-jelly-tags-xml
    1.1 as a test dependency, whose POM drags in dom4j:dom4j 1.5 and the XPP
    pull parser. The old coordinates are no longer overridden by the new
    ones and the XPP JAXP factory rejects secure processing, so both modules
    now depend on the in-tree 1.2-SNAPSHOT like the other tag libraries.
    
    Assisted-By: Claude Fable 5.1 <[email protected]>
    
    * Keep dom4j SAXReader from bypassing Commons Secure XML
    
    Unless an EntityResolver is set explicitly, `SAXReader.read` installs
    its own resolver, which resolves every external entity to an
    InputSource carrying only its system ID. The parser then fetches that
    system ID, which defeats the ignore-all resolver floor that Commons
    Secure XML installs on the XMLReader we hand to dom4j.
    
    Every SAXReader created by the xml and xmlunit tag libraries now gets a
    resolver that resolves nothing, so each lookup falls through to that
    floor. The three identical factory methods of the xmlunit tags move to
    a shared helper in XMLUnitTagSupport.
    
    Both modules gain a regression test that parses a document whose
    DOCTYPE points to a DTD that does not exist.
    
    Assisted-By: Claude Fable 5.1 <[email protected]>
---
 core/pom.xml                                       |  5 +-
 jelly-tags/jaxme/pom.xml                           |  2 +-
 .../commons/jelly/tags/jsl/ApplyTemplatesTag.java  |  4 +-
 .../apache/commons/jelly/tags/jsl/StyleTag.java    |  2 +-
 .../commons/jelly/tags/jsl/StylesheetTag.java      |  2 +-
 .../apache/commons/jelly/tags/jsl/Stylesheets.java | 78 ++++++++++++++++++++++
 .../apache/commons/jelly/tags/xml/ParseTag.java    | 10 ++-
 .../apache/commons/jelly/tags/xml/externalDtd.xml  | 20 ++++++
 .../org/apache/commons/jelly/tags/xml/suite.jelly  |  9 ++-
 jelly-tags/xmlunit/pom.xml                         |  2 +-
 .../commons/jelly/tags/xmlunit/ActualTag.java      | 11 +--
 .../tags/xmlunit/AssertDocumentsEqualTag.java      | 11 +--
 .../commons/jelly/tags/xmlunit/ExpectedTag.java    | 11 +--
 .../jelly/tags/xmlunit/XMLUnitTagSupport.java      | 24 +++++++
 .../jelly/tags/xmlunit/TestSecureSAXReader.java    | 46 +++++++++++++
 .../commons/jelly/tags/xmlunit/externalDtd.xml     | 20 ++++++
 pom.xml                                            |  5 +-
 src/changes/changes.xml                            |  2 +
 18 files changed, 221 insertions(+), 43 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 8c61ef84..9f2b39e1 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -92,9 +92,10 @@
       <version>${junit4.version}</version>
     </dependency>
     <dependency>
-      <groupId>dom4j</groupId>
+      <groupId>org.dom4j</groupId>
       <artifactId>dom4j</artifactId>
-      <version>1.6.1</version>
+      <!-- 2.1.x is the last line that runs on Java 8; 2.2.0 requires Java 11. 
-->
+      <version>2.1.5</version>
     </dependency>
     <dependency>
       <groupId>commons-beanutils</groupId>
diff --git a/jelly-tags/jaxme/pom.xml b/jelly-tags/jaxme/pom.xml
index 45178454..4ea5aad1 100644
--- a/jelly-tags/jaxme/pom.xml
+++ b/jelly-tags/jaxme/pom.xml
@@ -76,7 +76,7 @@
     <dependency>
       <artifactId>commons-jelly-tags-xml</artifactId>
       <groupId>commons-jelly</groupId>
-      <version>1.1</version>
+      <version>1.2-SNAPSHOT</version>
       <scope>test</scope>
     </dependency>
     <dependency>
diff --git 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/ApplyTemplatesTag.java
 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/ApplyTemplatesTag.java
index 30a708bf..4a3bc14a 100644
--- 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/ApplyTemplatesTag.java
+++ 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/ApplyTemplatesTag.java
@@ -60,10 +60,10 @@ public class ApplyTemplatesTag extends TagSupport {
         // for some reason, these DOM4J methods only throw Exception
         try {
             if ( select != null ) {
-                stylesheet.applyTemplates( source, select, mode );
+                Stylesheets.applyTemplates( stylesheet, source, select, mode );
             }
             else {
-                stylesheet.applyTemplates( source, mode );
+                Stylesheets.applyTemplates( stylesheet, source, mode );
             }
         }
         catch (final Exception e) {
diff --git 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StyleTag.java 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StyleTag.java
index b1ee4192..090b97d4 100644
--- 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StyleTag.java
+++ 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StyleTag.java
@@ -65,7 +65,7 @@ public class StyleTag extends XPathTagSupport {
                 log.debug("About to evaluate stylesheet on source: " + source);
             }
 
-            stylesheet.run(source);
+            Stylesheets.run(stylesheet, source);
         } catch (final Exception e) {
             throw new JellyTagException(e);
         }
diff --git 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StylesheetTag.java
 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StylesheetTag.java
index 076e16af..461b65be 100644
--- 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StylesheetTag.java
+++ 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/StylesheetTag.java
@@ -93,7 +93,7 @@ public class StylesheetTag extends XPathTagSupport implements 
XPathSource {
                     log.debug("About to evaluate stylesheet on source: " + 
source);
                 }
 
-                stylesheet.run(source);
+                Stylesheets.run(stylesheet, source);
             }
             catch (final Exception e) {
                 throw new JellyTagException(e);
diff --git 
a/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/Stylesheets.java
 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/Stylesheets.java
new file mode 100644
index 00000000..826a3bdd
--- /dev/null
+++ 
b/jelly-tags/jsl/src/main/java/org/apache/commons/jelly/tags/jsl/Stylesheets.java
@@ -0,0 +1,78 @@
+/*
+ * 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
+ *
+ *      https://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.commons.jelly.tags.jsl;
+
+import java.util.List;
+
+import org.dom4j.Node;
+import org.dom4j.rule.Stylesheet;
+import org.jaxen.XPath;
+
+/**
+ * Runs a {@link Stylesheet} on an untyped XPath context, which is either a 
{@link Node} or a {@link List} of nodes.
+ * <p>
+ * dom4j 1.x offered {@code Stylesheet.run(Object)} and {@code 
Stylesheet.applyTemplates(Object, ...)} overloads
+ * that dispatched on the runtime type of their input; dom4j 2.x only keeps 
the typed overloads.
+ * </p>
+ */
+final class Stylesheets {
+
+    /**
+     * Applies the templates of the given mode to the children of each node in 
the input.
+     */
+    static void applyTemplates(final Stylesheet stylesheet, final Object 
input, final String mode) throws Exception {
+        if (input instanceof Node) {
+            stylesheet.applyTemplates((Node) input, mode);
+        } else if (input instanceof List) {
+            for (final Object item : (List<?>) input) {
+                if (item instanceof Node) {
+                    stylesheet.applyTemplates((Node) item, mode);
+                }
+            }
+        }
+    }
+
+    /**
+     * Applies the templates of the given mode to the nodes selected by the 
XPath expression on the input.
+     */
+    static void applyTemplates(final Stylesheet stylesheet, final Object 
input, final XPath xpath, final String mode)
+            throws Exception {
+        for (final Object item : xpath.selectNodes(input)) {
+            if (item instanceof Node) {
+                stylesheet.run((Node) item, mode);
+            }
+        }
+    }
+
+    /**
+     * Runs the stylesheet in its current mode on each node in the input.
+     */
+    static void run(final Stylesheet stylesheet, final Object input) throws 
Exception {
+        if (input instanceof Node) {
+            stylesheet.run((Node) input);
+        } else if (input instanceof List) {
+            for (final Object item : (List<?>) input) {
+                if (item instanceof Node) {
+                    stylesheet.run((Node) item);
+                }
+            }
+        }
+    }
+
+    private Stylesheets() {
+    }
+}
diff --git 
a/jelly-tags/xml/src/main/java/org/apache/commons/jelly/tags/xml/ParseTag.java 
b/jelly-tags/xml/src/main/java/org/apache/commons/jelly/tags/xml/ParseTag.java
index a861da5b..8655cf36 100644
--- 
a/jelly-tags/xml/src/main/java/org/apache/commons/jelly/tags/xml/ParseTag.java
+++ 
b/jelly-tags/xml/src/main/java/org/apache/commons/jelly/tags/xml/ParseTag.java
@@ -54,9 +54,15 @@ public class ParseTag extends ParseTagSupport {
     protected SAXReader createSAXReader() throws SAXException {
         // dom4j builds its reader through JAXP internally; hand it one from 
the secure factory instead.
         try {
-            return new 
SAXReader(SecureSAXParserFactory.newNSInstance().newSAXParser().getXMLReader(), 
validate);
+            final SAXReader reader = new 
SAXReader(SecureSAXParserFactory.newNSInstance().newSAXParser().getXMLReader(), 
validate);
+            // Without an explicit EntityResolver, SAXReader installs one at 
read time that fetches every system ID.
+            // Resolving nothing instead hands each lookup to the ignore-all 
floor of Commons Secure XML.
+            reader.setEntityResolver((publicId, systemId) -> null);
+            return reader;
         } catch (final ParserConfigurationException e) {
-            throw new SAXException(e);
+            // Current JAXP implementations fail eagerly while the factory is 
configured, so these
+            // checked exceptions are not thrown in practice.
+            throw new IllegalStateException("Couldn't create SAX reader", e);
         }
     }
 
diff --git 
a/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/externalDtd.xml
 
b/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/externalDtd.xml
new file mode 100644
index 00000000..f70d7859
--- /dev/null
+++ 
b/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/externalDtd.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!--
+  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
+
+      https://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.
+-->
+<!-- The external DTD does not exist: parsing only succeeds if it is never 
fetched. -->
+<!DOCTYPE a SYSTEM "missing.dtd">
+<a>text</a>
diff --git 
a/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/suite.jelly
 
b/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/suite.jelly
index 92faaa0f..a7f81d58 100644
--- 
a/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/suite.jelly
+++ 
b/jelly-tags/xml/src/test/resources/org/apache/commons/jelly/tags/xml/suite.jelly
@@ -360,5 +360,12 @@
     <j:set var="value"><x:copyOf select="$doc/a/node()" 
lexical="true"/></j:set>
     <test:assertEquals actual="${value}" expected="&amp;x;y"/>
   </test:case>
-    
+
+  <test:case name="testExternalDtdIsNotFetched">
+    <!-- The DOCTYPE points to a DTD that does not exist: dom4j must leave it 
to the secure reader, which ignores it -->
+    <x:parse var="doc" xml="externalDtd.xml"/>
+    <j:set var="value"><x:copyOf select="$doc/a/node()"/></j:set>
+    <test:assertEquals actual="${value}" expected="text"/>
+  </test:case>
+
 </test:suite>
diff --git a/jelly-tags/xmlunit/pom.xml b/jelly-tags/xmlunit/pom.xml
index 2dd847ba..47cd431e 100644
--- a/jelly-tags/xmlunit/pom.xml
+++ b/jelly-tags/xmlunit/pom.xml
@@ -53,7 +53,7 @@
     <dependency>
       <groupId>commons-jelly</groupId>
       <artifactId>commons-jelly-tags-xml</artifactId>
-      <version>1.1</version>
+      <version>1.2-SNAPSHOT</version>
       <scope>test</scope>
     </dependency>
     <dependency>
diff --git 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ActualTag.java
 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ActualTag.java
index 383f8c73..be4faaf5 100644
--- 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ActualTag.java
+++ 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ActualTag.java
@@ -17,25 +17,16 @@
 
 package org.apache.commons.jelly.tags.xmlunit;
 
-import javax.xml.parsers.ParserConfigurationException;
-
 import org.apache.commons.jelly.JellyTagException;
 import org.apache.commons.jelly.XMLOutput;
-import org.apache.commons.xml.secure.SecureSAXParserFactory;
 import org.dom4j.Document;
 import org.dom4j.io.SAXReader;
-import org.xml.sax.SAXException;
 
 public class ActualTag extends XMLUnitTagSupport {
 
     @Override
     protected SAXReader createSAXReader() {
-        // dom4j builds its reader through JAXP internally; hand it one from 
the secure factory instead.
-        try {
-            return new 
SAXReader(SecureSAXParserFactory.newNSInstance().newSAXParser().getXMLReader());
-        } catch (final ParserConfigurationException | SAXException e) {
-            throw new IllegalStateException("Unable to create a new XML 
reader", e);
-        }
+        return createSecureSAXReader();
     }
 
     @Override
diff --git 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/AssertDocumentsEqualTag.java
 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/AssertDocumentsEqualTag.java
index a1a93c42..f4875c09 100644
--- 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/AssertDocumentsEqualTag.java
+++ 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/AssertDocumentsEqualTag.java
@@ -17,16 +17,12 @@
 
 package org.apache.commons.jelly.tags.xmlunit;
 
-import javax.xml.parsers.ParserConfigurationException;
-
 import org.apache.commons.jelly.JellyTagException;
 import org.apache.commons.jelly.XMLOutput;
-import org.apache.commons.xml.secure.SecureSAXParserFactory;
 import org.custommonkey.xmlunit.Diff;
 import org.custommonkey.xmlunit.XMLUnit;
 import org.dom4j.Document;
 import org.dom4j.io.SAXReader;
-import org.xml.sax.SAXException;
 
 /**
  * Compares two XML documents using XMLUnit (http://xmlunit.sourceforge.net/).
@@ -50,12 +46,7 @@ public class AssertDocumentsEqualTag extends 
XMLUnitTagSupport {
 
     @Override
     protected SAXReader createSAXReader() {
-        // dom4j builds its reader through JAXP internally; hand it one from 
the secure factory instead.
-        try {
-            return new 
SAXReader(SecureSAXParserFactory.newNSInstance().newSAXParser().getXMLReader());
-        } catch (final ParserConfigurationException | SAXException e) {
-            throw new IllegalStateException("Unable to create a new XML 
reader", e);
-        }
+        return createSecureSAXReader();
     }
 
     @Override
diff --git 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ExpectedTag.java
 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ExpectedTag.java
index 0913c149..5426da6b 100644
--- 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ExpectedTag.java
+++ 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/ExpectedTag.java
@@ -17,25 +17,16 @@
 
 package org.apache.commons.jelly.tags.xmlunit;
 
-import javax.xml.parsers.ParserConfigurationException;
-
 import org.apache.commons.jelly.JellyTagException;
 import org.apache.commons.jelly.XMLOutput;
-import org.apache.commons.xml.secure.SecureSAXParserFactory;
 import org.dom4j.Document;
 import org.dom4j.io.SAXReader;
-import org.xml.sax.SAXException;
 
 public class ExpectedTag extends XMLUnitTagSupport {
 
     @Override
     protected SAXReader createSAXReader() {
-        // dom4j builds its reader through JAXP internally; hand it one from 
the secure factory instead.
-        try {
-            return new 
SAXReader(SecureSAXParserFactory.newNSInstance().newSAXParser().getXMLReader());
-        } catch (final ParserConfigurationException | SAXException e) {
-            throw new IllegalStateException("Unable to create a new XML 
reader", e);
-        }
+        return createSecureSAXReader();
     }
 
     @Override
diff --git 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/XMLUnitTagSupport.java
 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/XMLUnitTagSupport.java
index 2380a20f..6b6aa270 100644
--- 
a/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/XMLUnitTagSupport.java
+++ 
b/jelly-tags/xmlunit/src/main/java/org/apache/commons/jelly/tags/xmlunit/XMLUnitTagSupport.java
@@ -21,9 +21,12 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.net.URL;
 
+import javax.xml.parsers.ParserConfigurationException;
+
 import org.apache.commons.jelly.JellyTagException;
 import org.apache.commons.jelly.XMLOutput;
 import org.apache.commons.jelly.tags.junit.AssertTagSupport;
+import org.apache.commons.xml.secure.SecureSAXParserFactory;
 import org.dom4j.Document;
 import org.dom4j.DocumentException;
 import org.dom4j.io.SAXContentHandler;
@@ -32,6 +35,27 @@ import org.xml.sax.SAXException;
 
 public abstract class XMLUnitTagSupport extends AssertTagSupport {
 
+    /**
+     * Creates a new SAXReader backed by an XMLReader from Commons Secure XML.
+     *
+     * @return a new SAXReader that does not fetch external entities.
+     * @throws IllegalStateException if no XML reader can be created.
+     */
+    protected static SAXReader createSecureSAXReader() {
+        // dom4j builds its reader through JAXP internally; hand it one from 
the secure factory instead.
+        try {
+            final SAXReader reader = new 
SAXReader(SecureSAXParserFactory.newNSInstance().newSAXParser().getXMLReader());
+            // Without an explicit EntityResolver, SAXReader installs one at 
read time that fetches every system ID.
+            // Resolving nothing instead hands each lookup to the ignore-all 
floor of Commons Secure XML.
+            reader.setEntityResolver((publicId, systemId) -> null);
+            return reader;
+        } catch (final ParserConfigurationException | SAXException e) {
+            // Current JAXP implementations fail eagerly while the factory is 
configured, so these
+            // checked exceptions are not thrown in practice.
+            throw new IllegalStateException("Unable to create a new XML 
reader", e);
+        }
+    }
+
     /** The SAXReader used to parser the document */
     private SAXReader saxReader;
 
diff --git 
a/jelly-tags/xmlunit/src/test/java/org/apache/commons/jelly/tags/xmlunit/TestSecureSAXReader.java
 
b/jelly-tags/xmlunit/src/test/java/org/apache/commons/jelly/tags/xmlunit/TestSecureSAXReader.java
new file mode 100644
index 00000000..b5f7f200
--- /dev/null
+++ 
b/jelly-tags/xmlunit/src/test/java/org/apache/commons/jelly/tags/xmlunit/TestSecureSAXReader.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ *
+ *      https://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.commons.jelly.tags.xmlunit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.InputStream;
+
+import org.dom4j.Document;
+import org.junit.Test;
+
+/**
+ * Tests the SAXReader created by {@link 
XMLUnitTagSupport#createSecureSAXReader()}.
+ */
+public class TestSecureSAXReader {
+
+    /**
+     * The DOCTYPE of the document points to a DTD that does not exist, so 
parsing only succeeds if dom4j leaves the
+     * external DTD to the ignore-all resolver of Commons Secure XML instead 
of installing its own resolver.
+     */
+    @Test
+    public void testExternalDtdIsNotFetched() throws Exception {
+        try (InputStream in = 
getClass().getResourceAsStream("externalDtd.xml")) {
+            assertNotNull("externalDtd.xml", in);
+            final Document document = 
XMLUnitTagSupport.createSecureSAXReader().read(in, "externalDtd.xml");
+            assertEquals("a", document.getRootElement().getName());
+            assertEquals("text", document.getRootElement().getText());
+        }
+    }
+}
diff --git 
a/jelly-tags/xmlunit/src/test/resources/org/apache/commons/jelly/tags/xmlunit/externalDtd.xml
 
b/jelly-tags/xmlunit/src/test/resources/org/apache/commons/jelly/tags/xmlunit/externalDtd.xml
new file mode 100644
index 00000000..f70d7859
--- /dev/null
+++ 
b/jelly-tags/xmlunit/src/test/resources/org/apache/commons/jelly/tags/xmlunit/externalDtd.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!--
+  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
+
+      https://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.
+-->
+<!-- The external DTD does not exist: parsing only succeeds if it is never 
fetched. -->
+<!DOCTYPE a SYSTEM "missing.dtd">
+<a>text</a>
diff --git a/pom.xml b/pom.xml
index d4e7b74b..9123c44b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -199,9 +199,10 @@
       <version>4.13.2</version>
     </dependency>
     <dependency>
-      <groupId>dom4j</groupId>
+      <groupId>org.dom4j</groupId>
       <artifactId>dom4j</artifactId>
-      <version>1.6.1</version>
+      <!-- 2.1.x is the last line that runs on Java 8; 2.2.0 requires Java 11. 
-->
+      <version>2.1.5</version>
     </dependency>
     <dependency>
       <groupId>commons-logging</groupId>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3e622249..167fb838 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.
       <action type="add" dev="ggregory" due-to="Gary Gregory">Extract commons 
version into new POM property ${mx4j.version}.</action>
       <!-- FIX -->
       <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Create XML parsers and readers through 
org.apache.commons:commons-secure-xml, so external entities and DTDs are no 
longer fetched unless JellyContext.setAllowDtdToCallExternalEntities(true) is 
used (#108).</action>
+      <action type="fix" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Set an EntityResolver on every dom4j SAXReader, so its default 
resolver no longer fetches external DTDs and entities behind the back of 
commons-secure-xml.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Throw 
specialized RuntimeExceptions instead of plain RuntimeExceptions.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Deprecate 
NestedRuntimeException for RuntimeException.</action>
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix building on 
modern Java.</action>
@@ -56,6 +57,7 @@ The <action> type attribute can be add,update,fix,remove.
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump Java 
from 7 to 8.</action>
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
commons-parent from 42 to 105.</action>
       <action type="update" dev="sebb" >Update jaxen:jaxen 1.1.6 => 
2.0.0</action>
+      <action type="update" dev="pkarwasz" due-to="Piotr P. Karwasz, Gary 
Gregory">Bump dom4j:dom4j from 1.6.1 to org.dom4j:dom4j 2.1.5, the last release 
that runs on Java 8.</action>
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
commons-logging from 1.0.3 to 1.4.0.</action>
       <action type="update" dev="ggregory" due-to="Gary Gregory">Bump 
commons-beanutils:commons-beanutils from a mix of 1.6 and 1.7.0 to 
org.apache.commons:commons-beanutils2 2.0.0-M2.
       Update configurations like:

Reply via email to