
Runnable proof-of-concept reproducer for CVE-2026-88789, demonstrating XXE and SSRF in Apache Camel Quarkus camel-quarkus-support-xalan via the XSLT TransformerFactory.
TransformerFactory drops the JAXP external access restrictionsRunnable proof-of-concept reproducer for the Apache Camel Quarkus vulnerability where the XSLT support
extension (camel-quarkus-support-xalan) supplies its own Xalan-backed TransformerFactory to the xslt
component and registers it as the JAXP default. Xalan-J 2.7.x predates JAXP 1.5 and cannot honour
javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD or ACCESS_EXTERNAL_STYLESHEET — setAttribute() throws
IllegalArgumentException for both — so the external access restrictions Apache Camel applies to the
TransformerFactory it creates were never in effect.
| Runtime | Directory | Stack |
|---|---|---|
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.36.0 (Quarkus 3.36.0, Camel 4.20.0) |
Camel Quarkus only. The vulnerable code is a Camel Quarkus extension, not a Camel component. Plain Camel and Camel Spring Boot use the JDK's
TransformerFactory, which honours both attributes, so there is nothing to reproduce there — this repository therefore has nocamel-spring-boot/variant.
An attacker who supplies the XML document being transformed can read local files or reach internal network locations through an external entity declaration in that document.
cd camel-quarkus
mvn clean package
docker compose up -d --build
curl -s http://localhost:8080/exploit/attack
docker compose down
Expected output on an affected build (abridged — the driver runs six probes, see
camel-quarkus/README.md):
1) xslt endpoint, body is a StreamSource, external entity -> file:///tmp/cve-2026-88789-secrets/db-password.txt
transformation result: [db.password=LOCAL-FILE-s3cr3t-99]
local file contents in the output: true
2) xslt endpoint, body is a StreamSource, external entity -> http://127.0.0.1:8080/internal/secret
transformation result: [INTERNAL-SECRET-s3cr3t-42]
internal endpoint response in the output: true
4) CONTROL - same document as a String body (Camel converts it to a SAXSource itself)
transformation result: []
local file contents in the output: false
5) TransformerFactory.newInstance() anywhere in the application
factory: org.apache.camel.quarkus.support.xalan.XalanTransformerFactory
setAttribute(ACCESS_EXTERNAL_DTD, ""): REFUSED, IllegalArgumentException: ...
identity transform of the same document: [... <data>db.password=LOCAL-FILE-s3cr3t-99</data> ...]
Requests the XML parser made to internal endpoints on its own: [GET /internal/secret, GET /internal/leak.dtd]
>>> PROVEN: ...
Verified against Camel Quarkus 3.40.0 as well: every leaking probe goes quiet, the internal endpoints
receive no requests at all, and the driver prints NOT reproduced.
On the xslt component path, only bodies that reach the transformer already as a
javax.xml.transform.Source are affected. Bodies of other types — String, byte[], InputStream — are
converted by Apache Camel to a SAXSource with external entities and external DTD loading disabled, and are
not affected. Probe 4 in the reproducer is that safe path, side by side with the unsafe one.
Because the factory is also registered as the JAXP default (the support extension ships
META-INF/services/javax.xml.transform.TransformerFactory), any other code in the application that obtains
a factory through TransformerFactory.newInstance() loses the same restrictions, without an error. That is
why the advisory lists extensions that never transform anything themselves:
| Extension | Exposure |
|---|---|
camel-quarkus-xslt | the xslt component path and the JAXP default |
camel-quarkus-xslt-saxon | the JAXP default |
camel-quarkus-tika | the JAXP default |
camel-quarkus-xmlsecurity | the JAXP default |
| Property | Value |
|---|---|
| Component | camel-quarkus-support-xalan (XSLT support extension) |
| CWE | CWE-611 (Improper Restriction of XML External Entity Reference) |
| Severity | High |
| Attack vector | An external entity or external DTD declared in the XML document being transformed, where the body reaches the xslt endpoint already as a javax.xml.transform.Source |
| Impact | Read local files; issue requests to internal network locations (SSRF) |
| Affected Versions | From 3.2.0 before 3.33.3, from 3.34.0 before 3.40.0 |
| Fixed Versions | 3.33.3 (LTS stream), 3.40.0 |
| GitHub issue | apache/camel-quarkus#9115 |
| Credit | Discovered by internal analysis, using Claude Security Tool |
Advisory: https://camel.apache.org/security/CVE-2026-88789.html
XalanTransformerFactory now applies the restrictions itself instead of relying on attributes Xalan cannot
honour:
XMLReader that resolves neither external general nor
external parameter entities and does not load external DTDs — the same configuration Apache Camel's
XmlConverter.createSAXParserFactory() uses for the bodies camel-xslt converts to a SAXSource itself.
A SAXSource carrying a caller-configured XMLReader is used as it is, and DOMSource and StAXSource
are already parsed.document() function are denied unless the application's own
URIResolver resolves them, and the restriction is installed on every entry point that hands out something
to transform with, including the SAX push entry points whose transformers Xalan does not copy the factory
resolver onto. Applications that set their own resolver — camel-xslt does so on every exchange — keep
overriding it as before.Fixed on main in
9a570b64 and
9dd11779,
backported to 3.33.x in
ad9c5236 and
3d886769.
javax.xml.transform.Source built from untrusted input into an xslt endpoint. Leave the
message body as String, byte[] or InputStream so that Apache Camel converts it to a SAXSource with
external entities disabled first.convertBodyTo on an existing Source body is not a workaround: that conversion performs an identity
transform through the same factory. Probe 5 in the reproducer is that identity transform, and it leaks.com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl,
rather than relying on TransformerFactory.newInstance(). Probe 6 is that control, and it denies the read.This repository is published for educational and defensive purposes: to help Apache Camel Quarkus users understand the vulnerability, verify whether they are affected, and confirm that upgrading resolves it. Do not use this material against systems you do not own or operate.