Skip to content

Commit 546e64d

Browse files
committed
Java: update the Commons XML model to the commons-secure-xml 1.0.0 API
The library was renamed from commons-xml to commons-secure-xml: the org.apache.commons.xml.XmlFactories entry point is replaced by six per-factory classes in org.apache.commons.xml.secure, whose static new* methods mirror the JAXP factory entry points and all return hardened factories. Assisted-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BiNxFje3KZrioKjQTcBCyk
1 parent 5f1e8b8 commit 546e64d

12 files changed

Lines changed: 216 additions & 66 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: feature
3+
---
4+
* Factories returned by the Apache Commons Secure XML (`org.apache.commons.xml.secure`) hardening library's `SecureDocumentBuilderFactory`, `SecureSAXParserFactory`, `SecureXMLInputFactory`, `SecureTransformerFactory` and `SecureSchemaFactory` classes are now recognized as safely configured by the XXE query.
5+
* A new extensible class `SafeXmlFactorySource` was added to `semmle.code.java.security.XmlParsers` for modeling sources of pre-hardened JAXP factories.

‎java/ql/lib/change-notes/2026-08-02-apache-commons-xml-factories.md‎

Lines changed: 0 additions & 5 deletions
This file was deleted.

‎java/ql/lib/semmle/code/java/frameworks/apache/CommonsXml.qll‎

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,31 @@ private module SafeDigesterFlowConfig implements DataFlow::ConfigSig {
9292
private module SafeDigesterFlow = DataFlow::Global<SafeDigesterFlowConfig>;
9393

9494
/**
95-
* A call to one of the `org.apache.commons.xml.XmlFactories.newXxxFactory()` methods
96-
* of the Apache Commons XML library.
95+
* A call to one of the static factory methods of the `org.apache.commons.xml.secure`
96+
* `SecureXxxFactory` classes of the Apache Commons Secure XML library.
9797
*
98-
* Every such method returns a fresh JAXP factory that has already been hardened against
99-
* XML external entity (XXE) attacks, so any parser created from it is treated as safe.
98+
* These methods mirror the JAXP factory entry points (`newInstance`, `newDefaultInstance`,
99+
* `newNSInstance`, `newFactory`, ...) and every one of them returns a fresh JAXP factory
100+
* that has already been hardened against XML external entity (XXE) attacks, so any parser
101+
* created from it is treated as safe.
100102
*
101-
* `newXPathFactory` is matched for completeness, but the XXE model has no `XPathFactory`
103+
* `SecureXPathFactory` is matched for completeness, but the XXE model has no `XPathFactory`
102104
* safety chain (the XXE sink for XPath is the document being evaluated, not the factory),
103105
* so it currently has no effect on XXE results.
104106
*/
105-
private class CommonsXmlSafeXmlFactory extends SafeXmlFactorySource, MethodCall {
106-
CommonsXmlSafeXmlFactory() {
107-
this.getMethod().getDeclaringType().hasQualifiedName("org.apache.commons.xml", "XmlFactories") and
107+
private class CommonsSecureXmlFactory extends SafeXmlFactorySource, MethodCall {
108+
CommonsSecureXmlFactory() {
109+
this.getMethod()
110+
.getDeclaringType()
111+
.hasQualifiedName("org.apache.commons.xml.secure",
112+
[
113+
"SecureDocumentBuilderFactory", "SecureSAXParserFactory", "SecureXMLInputFactory",
114+
"SecureTransformerFactory", "SecureSchemaFactory", "SecureXPathFactory"
115+
]) and
108116
this.getMethod()
109117
.hasName([
110-
"newDocumentBuilderFactory", "newSAXParserFactory", "newXMLInputFactory",
111-
"newTransformerFactory", "newSchemaFactory", "newXPathFactory"
118+
"newDefaultInstance", "newDefaultNSInstance", "newInstance", "newNSInstance",
119+
"newDefaultFactory", "newFactory"
112120
])
113121
}
114122
}

java/ql/test/query-tests/security/CWE-611/XmlFactoriesTests.java renamed to java/ql/test/query-tests/security/CWE-611/SecureXmlFactoriesTests.java

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,49 +15,63 @@
1515
import org.xml.sax.XMLReader;
1616
import org.xml.sax.helpers.DefaultHandler;
1717

18-
import org.apache.commons.xml.XmlFactories;
18+
import org.apache.commons.xml.secure.SecureDocumentBuilderFactory;
19+
import org.apache.commons.xml.secure.SecureSAXParserFactory;
20+
import org.apache.commons.xml.secure.SecureSchemaFactory;
21+
import org.apache.commons.xml.secure.SecureTransformerFactory;
22+
import org.apache.commons.xml.secure.SecureXMLInputFactory;
1923

20-
// Every factory returned by `org.apache.commons.xml.XmlFactories` is already hardened against
21-
// XXE, so the parsers created from them must not be reported.
22-
public class XmlFactoriesTests {
24+
// Every factory returned by the `org.apache.commons.xml.secure.SecureXxxFactory` classes is
25+
// already hardened against XXE, so the parsers created from them must not be reported.
26+
public class SecureXmlFactoriesTests {
2327

2428
public void hardenedDocumentBuilder(Socket sock) throws Exception {
25-
DocumentBuilderFactory factory = XmlFactories.newDocumentBuilderFactory();
29+
DocumentBuilderFactory factory = SecureDocumentBuilderFactory.newInstance();
2630
DocumentBuilder builder = factory.newDocumentBuilder();
2731
builder.parse(sock.getInputStream()); // safe
2832
}
2933

3034
public void hardenedDocumentBuilderChained(Socket sock) throws Exception {
31-
XmlFactories.newDocumentBuilderFactory().newDocumentBuilder().parse(sock.getInputStream()); // safe
35+
SecureDocumentBuilderFactory.newDefaultNSInstance().newDocumentBuilder().parse(sock.getInputStream()); // safe
3236
}
3337

3438
public void hardenedSaxParser(Socket sock) throws Exception {
35-
SAXParserFactory factory = XmlFactories.newSAXParserFactory();
39+
SAXParserFactory factory = SecureSAXParserFactory.newInstance();
3640
SAXParser parser = factory.newSAXParser();
3741
parser.parse(sock.getInputStream(), new DefaultHandler()); // safe
3842
}
3943

4044
public void hardenedSaxParserXmlReader(Socket sock) throws Exception {
41-
SAXParser parser = XmlFactories.newSAXParserFactory().newSAXParser();
45+
SAXParser parser = SecureSAXParserFactory.newNSInstance().newSAXParser();
4246
XMLReader reader = parser.getXMLReader();
4347
reader.parse(new org.xml.sax.InputSource(sock.getInputStream())); // safe
4448
}
4549

4650
public void hardenedXmlInputFactory(Socket sock) throws Exception {
47-
XMLInputFactory factory = XmlFactories.newXMLInputFactory();
51+
XMLInputFactory factory = SecureXMLInputFactory.newFactory();
4852
factory.createXMLStreamReader(sock.getInputStream()); // safe
4953
factory.createXMLEventReader(sock.getInputStream()); // safe
5054
}
5155

56+
public void hardenedXmlInputFactoryDefault(Socket sock) throws Exception {
57+
XMLInputFactory factory = SecureXMLInputFactory.newDefaultFactory();
58+
factory.createXMLStreamReader(sock.getInputStream()); // safe
59+
}
60+
5261
public void hardenedTransformer(Socket sock) throws Exception {
53-
TransformerFactory tf = XmlFactories.newTransformerFactory();
62+
TransformerFactory tf = SecureTransformerFactory.newInstance();
5463
Transformer transformer = tf.newTransformer();
5564
transformer.transform(new StreamSource(sock.getInputStream()), null); // safe
5665
tf.newTransformer(new StreamSource(sock.getInputStream())); // safe
5766
}
5867

68+
public void hardenedTransformerDefault(Socket sock) throws Exception {
69+
TransformerFactory tf = SecureTransformerFactory.newDefaultInstance();
70+
tf.newTransformer(new StreamSource(sock.getInputStream())); // safe
71+
}
72+
5973
public void hardenedSchema(Socket sock) throws Exception {
60-
SchemaFactory factory = XmlFactories.newSchemaFactory(XMLConstants.W3C_XML_SCHEMA_NS_URI);
74+
SchemaFactory factory = SecureSchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
6175
Schema schema = factory.newSchema(new StreamSource(sock.getInputStream())); // safe
6276
}
6377
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jdom-1.1.3:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/simple-xml-2.7.1:${testdir}/../../../stubs/jaxb-api-2.3.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/apache-commons-digester3-3.2:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/rundeck-api-java-client-13.2:${testdir}/../../../stubs/springframework-5.8.x/:${testdir}/../../../stubs/mdht-1.2.0/:${testdir}/../../../stubs/woodstox-core-6.4.0:${testdir}/../../../stubs/apache-commons-xml-0.1.0
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jdom-1.1.3:${testdir}/../../../stubs/dom4j-2.1.1:${testdir}/../../../stubs/simple-xml-2.7.1:${testdir}/../../../stubs/jaxb-api-2.3.1:${testdir}/../../../stubs/jaxen-1.2.0:${testdir}/../../../stubs/apache-commons-digester3-3.2:${testdir}/../../../stubs/servlet-api-2.4/:${testdir}/../../../stubs/rundeck-api-java-client-13.2:${testdir}/../../../stubs/springframework-5.8.x/:${testdir}/../../../stubs/mdht-1.2.0/:${testdir}/../../../stubs/woodstox-core-6.4.0:${testdir}/../../../stubs/apache-commons-secure-xml-1.0.0

‎java/ql/test/stubs/apache-commons-secure-xml-1.0.0/org/apache/commons/xml/secure/SecureDocumentBuilderFactory.java‎

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎java/ql/test/stubs/apache-commons-secure-xml-1.0.0/org/apache/commons/xml/secure/SecureSAXParserFactory.java‎

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎java/ql/test/stubs/apache-commons-secure-xml-1.0.0/org/apache/commons/xml/secure/SecureSchemaFactory.java‎

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎java/ql/test/stubs/apache-commons-secure-xml-1.0.0/org/apache/commons/xml/secure/SecureTransformerFactory.java‎

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎java/ql/test/stubs/apache-commons-secure-xml-1.0.0/org/apache/commons/xml/secure/SecureXMLInputFactory.java‎

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)