From 9532f90727947099e67e30a39b77111a73d05ca6 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Thu, 3 Sep 2026 09:28:11 +0200 Subject: [PATCH 1/2] Harden GMLReader against XXE (use Commons Secure XML) GMLReader created its SAX parser through a stock SAXParserFactory with only namespace awareness and validation switched off, so a document could declare external entities and have the parser fetch them. Create the factory through Apache Commons Secure XML instead, which guarantees on every JAXP implementation that external DTDs and entities are never resolved and that entity expansion is bounded. Enforce the choice with a forbidden-apis check that rejects the raw SAX factory methods in `jts-core`, and link the Javadoc to the library's documentation. Commons Secure XML 1.0.0 is under vote: the POM temporarily adds the Apache staging repository after Central; remove it once the release reaches Maven Central. Assisted-By: Claude Fable 5.1 Signed-off-by: Piotr P. Karwasz --- modules/core/pom.xml | 24 ++++++++++++++ .../core/src/conf/forbidden-sax-factories.txt | 12 +++++++ .../locationtech/jts/io/gml2/GMLReader.java | 15 ++++++--- pom.xml | 32 +++++++++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 modules/core/src/conf/forbidden-sax-factories.txt diff --git a/modules/core/pom.xml b/modules/core/pom.xml index 4a2b8d64ff..434782ebb7 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -14,8 +14,32 @@ none + + + org.apache.commons + commons-secure-xml + + + + + de.thetaphi + forbiddenapis + + false + + ${project.basedir}/src/conf/forbidden-sax-factories.txt + + + + + + check + + + + maven-jar-plugin diff --git a/modules/core/src/conf/forbidden-sax-factories.txt b/modules/core/src/conf/forbidden-sax-factories.txt new file mode 100644 index 0000000000..a809008038 --- /dev/null +++ b/modules/core/src/conf/forbidden-sax-factories.txt @@ -0,0 +1,12 @@ +# Raw SAX factories parse with the implementation's defaults, which fetch external DTDs +# and resolve external entities. Create SAX parsers through Apache Commons Secure XML instead. +@defaultMessage Create SAX parsers through org.apache.commons.xml.secure.SecureSAXParserFactory, which never resolves external DTDs or entities +@ignoreUnresolvable +javax.xml.parsers.SAXParserFactory#newInstance() +javax.xml.parsers.SAXParserFactory#newInstance(java.lang.String,java.lang.ClassLoader) +javax.xml.parsers.SAXParserFactory#newDefaultInstance() +javax.xml.parsers.SAXParserFactory#newDefaultNSInstance() +javax.xml.parsers.SAXParserFactory#newNSInstance() +javax.xml.parsers.SAXParserFactory#newNSInstance(java.lang.String,java.lang.ClassLoader) +org.xml.sax.helpers.XMLReaderFactory#createXMLReader() +org.xml.sax.helpers.XMLReaderFactory#createXMLReader(java.lang.String) diff --git a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLReader.java b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLReader.java index 7bef25880f..ac706b6806 100644 --- a/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLReader.java +++ b/modules/core/src/main/java/org/locationtech/jts/io/gml2/GMLReader.java @@ -19,6 +19,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import org.apache.commons.xml.secure.SecureSAXParserFactory; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryCollection; import org.locationtech.jts.geom.GeometryFactory; @@ -49,9 +50,15 @@ * * The reader ignores namespace prefixes, * and disables both the validation and namespace options on the SAXParser. - * This class requires the presence of a SAX Parser available via the - * {@link javax.xml.parsers.SAXParserFactory#newInstance()} - * method. + *

+ * The parser is created by {@link SecureSAXParserFactory} from Apache Commons Secure XML, + * so external DTDs and external entities are never resolved + * and internal entity expansion is bounded, + * whichever SAX implementation is on the classpath. + * See its package documentation + * for the guarantees + * and the threat model + * for what remains the caller's responsibility. *

* A specification of the GML XML format * can be found at the OGC web site: http://www.opengeospatial.org/. @@ -101,7 +108,7 @@ public Geometry read(String gml, GeometryFactory geometryFactory) throws SAXExce * @throws IOException */ public Geometry read(Reader reader, GeometryFactory geometryFactory) throws SAXException, IOException, ParserConfigurationException{ - SAXParserFactory fact = SAXParserFactory.newInstance(); + SAXParserFactory fact = SecureSAXParserFactory.newInstance(); fact.setNamespaceAware(false); fact.setValidating(false); diff --git a/pom.xml b/pom.xml index 09dd96bddb..f4c59d95dd 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,7 @@ UTF-8 + 1.0.0 4.13.1 2.0.6.1 1.2 @@ -106,6 +107,11 @@ junit ${junit-version} + + org.apache.commons + commons-secure-xml + ${commons-secure-xml-version} + org.jdom jdom2 @@ -151,6 +157,24 @@ false + + + central + Central Repository + https://repo.maven.apache.org/maven2 + + false + + + + + apache.commons.staging + Apache Commons Secure XML 1.0.0 release candidate + https://repository.apache.org/content/repositories/orgapachecommons-1962/ + + false + + @@ -215,6 +239,11 @@ maven-checkstyle-plugin 3.1.2 + + de.thetaphi + forbiddenapis + 3.10 + maven-compiler-plugin 3.8.1 @@ -308,6 +337,9 @@

${project.name} ${project.version}
org.locationtech.jtsexample.*,org.locationtech.jtstest,org.locationtech.jtstest.* + + https://javadoc.io/doc/org.apache.commons/commons-secure-xml/${commons-secure-xml-version} + Core - Geometry From 404585d2cca14140c9a6706b3f02017b606c1784 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Mon, 7 Sep 2026 22:56:09 +0200 Subject: [PATCH 2/2] fix: `commons-secure-xml` was released --- pom.xml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pom.xml b/pom.xml index f4c59d95dd..84f7d5868e 100644 --- a/pom.xml +++ b/pom.xml @@ -157,24 +157,6 @@ false - - - central - Central Repository - https://repo.maven.apache.org/maven2 - - false - - - - - apache.commons.staging - Apache Commons Secure XML 1.0.0 release candidate - https://repository.apache.org/content/repositories/orgapachecommons-1962/ - - false - -