Java: model org.apache.commons.xml XmlFactories as safe XXE sources - #22269
Open
ppkarwasz wants to merge 1 commit into
Open
Java: model org.apache.commons.xml XmlFactories as safe XXE sources#22269ppkarwasz wants to merge 1 commit into
ppkarwasz wants to merge 1 commit into
Conversation
Recognize the hardened JAXP factories returned by `org.apache.commons.xml.XmlFactories` (Apache Commons XML) as safely configured, so parsers created from them are no longer reported by the XXE query (CWE-611). Adds an extensible `SafeXmlFactorySource` class to XmlParsers.qll and wires it into the five existing safe-factory flow sources (DocumentBuilderFactory, SAXParserFactory, XMLInputFactory, TransformerFactory, SchemaFactory). `newXPathFactory` is matched for completeness but has no XXE safety chain to feed. The framework model lives in the existing frameworks/apache/CommonsXml.qll. Includes a test stub, safe-case tests, and a change note. Assisted-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds Apache Commons XML factories as pre-hardened XXE-safe sources in Java analysis.
Changes:
- Introduces an extensible safe-factory source model.
- Models all six
XmlFactoriesfactory methods. - Adds stubs, XXE regression tests, and a change note.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
XmlFactories.java |
Adds the Commons XML test stub. |
XmlFactoriesTests.java |
Tests safe parser flows from hardened factories. |
options |
Adds the stub to the test classpath. |
XmlParsers.qll |
Adds and connects the safe-factory extension point. |
CommonsXml.qll |
Models Commons XML factory calls. |
2026-08-02-apache-commons-xml-factories.md |
Documents the feature. |
| return null; | ||
| } | ||
|
|
||
| public static SchemaFactory newSchemaFactory() { |
Author
There was a problem hiding this comment.
Thanks for the reminder: I'll merge both suggestions as soon as the apache/commons-xml#32 is merged itself.
Since the factory method is matched by name, not by signature, this will not have any effect on the QL code.
| } | ||
|
|
||
| public void hardenedSchema(Socket sock) throws Exception { | ||
| SchemaFactory factory = XmlFactories.newSchemaFactory(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
We are about to publish the first release of Apache Commons XML, a new Apache Commons component that provides secure-by-default creation of JAXP factories. Its
org.apache.commons.xml.XmlFactoriesentry point returns factories that are hardened against XXE and SSRF regardless of which JAXP implementation is on the classpath (JDK, Android, Xalan, Xerces, Woodstox, Saxon-HE). This differs from the usual approach of setting implementation-specific hardening features on whatever factorynewInstance()returns: when the implementation does not support a given feature, that approach silently fails open, whereas Commons XML detects the implementation and applies a hardening strategy known to work for it, failing closed otherwise.Beyond the technical guarantee, an equally important goal of the library is to reduce the security-report noise around XML parsing of trusted documents, such as configuration files. Maintainers of libraries that parse such files regularly receive reports of alleged XXE vulnerabilities, and each SAST tool expects a slightly different set of hardening features before it stops flagging a JAXP factory call. Centralizing the hardening in one well-audited place gives maintainers a single answer to those reports, and gives SAST tools a single API to recognize.
Why open this PR before the first release?
That last point is exactly why we are opening this PR now rather than after adoption picks up. The library only delivers its value if security tooling recognizes it: otherwise every adoption creates alerts instead of removing them. We saw this with the library's prototype,
copernik-xml-factory: when I tested it in the Apache Log4j project I maintain, CodeQL raised an XXE alert on the very code the library exists to make safe (apache/logging-log4j2#4144 (comment)), and every future adopter would have to triage and dismiss the same false positive or ship a custom model pack.Modeling the library ahead of its 0.1.0 release means early adopters get correct CodeQL results from day one. The API surface modeled here (the six static
newXxxFactory()methods) is final for the first release, so the model is not chasing a moving target and the apache/commons-xml#32 API correction I proposed today does not require changes in the CodeQL code.Concretely: every factory returned by
org.apache.commons.xml.XmlFactoriesis already hardened against XML external entity (XXE) attacks, but the XXE query (java/xxe, CWE-611) does not know this and reports parsers created from these factories as vulnerable.This PR teaches the XXE query that such factories are safely configured:
SafeXmlFactorySourcetosemmle.code.java.security.XmlParsers, representing an expression that evaluates to a JAXP factory that is already hardened against XXE (for example by a helper library). It is wired into the five existing safe-factory flow sources (DocumentBuilderFactory,SAXParserFactory,XMLInputFactory,TransformerFactory,SchemaFactory), dispatching on the static type of the expression.semmle.code.java.frameworks.apache.CommonsXml(which already models Commons Digester for XXE) with aSafeXmlFactorySourcesubclass matching calls to theXmlFactories.newXxxFactory()methods.newXPathFactoryis matched for completeness, but the XXE model has noXPathFactorysafety chain (the XXE sink for XPath is the document being evaluated, not the factory), so it currently has no effect on results.Note
I do not write QL myself. This PR was created with the help of Claude Code (
claude-opus-4-8andclaude-fable-5). I have reviewed every change semantically, that is, which calls are modeled and why they are safe to treat as hardened, but I cannot vouch for QL idiom or style, so feedback on that level is especially welcome.Tests
java/ql/test/query-tests/security/CWE-611/XmlFactoriesTests.javacovering parsers created from each of the hardened factories (direct, chained, and viaXMLReader). All cases are safe, soXXE.expectedis unchanged; the test fails with new#selectrows if any case is still flagged.java/ql/test/stubs/apache-commons-xml-0.1.0/following the existingapache-commons-<name>-<version>naming convention, added to the CWE-611 extractor classpath.codeql test run java/ql/test/query-tests/security/CWE-611passes (1/1).Checklist
java/ql/lib/change-notes/2026-08-02-apache-commons-xml-factories.md, categoryfeaturesinceSafeXmlFactorySourceis a new public API).codeql query format --check-onlypasses).getAQlClass, or regexp matching ontoString.python config/add-overlay-annotations.py --check javapasses).