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="&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: