Summary
Several core utility classes parse caller-supplied XML through `DocumentBuilderFactory` or pass it through a `Transformer` over a `StreamSource` without enabling `FEATURE_SECURE_PROCESSING` or disabling DTD / external entity resolution. Default JAXP behavior allows DOCTYPE and external entities, exposing XXE (CWE-611) and billion-laughs-style entity expansion (CWE-776) when callers pass untrusted input.
Discovered via automated SAST sweep (cognium-ai 2.18.3) over top-100 starred Java repos on GitHub. `xdocreport` consumes `.docx`/`.odt`/`.pptx` files (which are zipped XML) and is widely used as a document-template engine; these utilities are exposed throughout the pipeline.
Vulnerable sites
1. `core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/utils/DOMUtils.java:59, 68`
```java
public static Document load(InputStream stream)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // ← no hardening
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(stream); // ← XXE
}
public static Document load(String xml)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // ← no hardening
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(IOUtils.toInputStream(xml, EncodingConstants.UTF_8.name())); // ← XXE
}
```
These are public, static utility methods called from many places within xdocreport to load XML fragments from `.docx`/`.odt` contents and other sources.
2. `core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/internal/XSLTPrettyPrinter.java:69`
```java
public String prettyPrint(String xml, int indent) throws Exception {
Transformer transformer = getTemplates(indent).newTransformer();
final StringWriter out = new StringWriter();
transformer.transform(new StreamSource(new StringReader(xml)), // ← XXE: untrusted xml via StreamSource
new StreamResult(out));
return out.toString();
}
```
Although the result is `StreamResult` (output-only), the `StreamSource` over caller-supplied `xml` is read by an XML parser inside the Transformer pipeline — DOCTYPE/entities still get resolved unless secure processing is enabled on the `TransformerFactory` that built the `Templates`.
3. `core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/internal/IndentNumberPrettyPrinter.java:68`
```java
public String prettyPrint(String xml, int indent) throws Exception {
TransformerFactory factory = TransformerFactory.newInstance(); // ← no hardening
factory.setAttribute(INDENT_NUMBER, indent);
Transformer transformer = factory.newTransformer();
// ...
transformer.transform(new StreamSource(new StringReader(xml)), // ← XXE
new StreamResult(out));
}
```
Same pattern — untrusted XML in via `StreamSource`, no `FEATURE_SECURE_PROCESSING` on the `TransformerFactory`.
`NoIndentNumberPrettyPrinter` (similar class in the same package) and `DocumentBuilderFactory` use in `docx/DocxReport.java:165` and `xslt/DefaultTransformerFactory.java:45` (in the FOP sandbox module) likely have the same shape — recommend reviewing all `DocumentBuilderFactory` / `TransformerFactory` constructions in the codebase.
Impact
- Out-of-band data exfiltration via external entity references (``) when xdocreport is fed an attacker-crafted .docx/.odt file or XML fragment.
- Denial of service via billion-laughs / parameter-entity expansion in document processing.
- Server-side request forgery via external DTD fetches.
The DOMUtils.load methods being public static + namespace-aware suggests they are entry points used wherever xdocreport reads document XML — a malicious .docx/.odt uploaded to any application using xdocreport for templating could trigger this.
Recommended fix
Apply JAXP hardening on all `DocumentBuilderFactory` and `TransformerFactory` instances at construction:
```java
// DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl\", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities\", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities\", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd\", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
// TransformerFactory
TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
```
A centralized `SafeXmlFactories` helper class in `fr.opensagres.xdocreport.core.utils` would let every call site share one hardened factory.
References:
Discovery attribution
Found by cognium-ai SAST during a top-100 Java repo sweep. Total 9 XXE findings in xdocreport — the 3+ above are confirmed by code read; the remaining sites likely follow the same pattern. Happy to review a patch.
Summary
Several core utility classes parse caller-supplied XML through `DocumentBuilderFactory` or pass it through a `Transformer` over a `StreamSource` without enabling `FEATURE_SECURE_PROCESSING` or disabling DTD / external entity resolution. Default JAXP behavior allows DOCTYPE and external entities, exposing XXE (CWE-611) and billion-laughs-style entity expansion (CWE-776) when callers pass untrusted input.
Discovered via automated SAST sweep (cognium-ai 2.18.3) over top-100 starred Java repos on GitHub. `xdocreport` consumes `.docx`/`.odt`/`.pptx` files (which are zipped XML) and is widely used as a document-template engine; these utilities are exposed throughout the pipeline.
Vulnerable sites
1. `core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/utils/DOMUtils.java:59, 68`
```java
public static Document load(InputStream stream)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // ← no hardening
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(stream); // ← XXE
}
public static Document load(String xml)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // ← no hardening
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(IOUtils.toInputStream(xml, EncodingConstants.UTF_8.name())); // ← XXE
}
```
These are public, static utility methods called from many places within xdocreport to load XML fragments from `.docx`/`.odt` contents and other sources.
2. `core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/internal/XSLTPrettyPrinter.java:69`
```java
public String prettyPrint(String xml, int indent) throws Exception {
Transformer transformer = getTemplates(indent).newTransformer();
final StringWriter out = new StringWriter();
transformer.transform(new StreamSource(new StringReader(xml)), // ← XXE: untrusted xml via StreamSource
new StreamResult(out));
return out.toString();
}
```
Although the result is `StreamResult` (output-only), the `StreamSource` over caller-supplied `xml` is read by an XML parser inside the Transformer pipeline — DOCTYPE/entities still get resolved unless secure processing is enabled on the `TransformerFactory` that built the `Templates`.
3. `core/fr.opensagres.xdocreport.core/src/main/java/fr/opensagres/xdocreport/core/internal/IndentNumberPrettyPrinter.java:68`
```java
public String prettyPrint(String xml, int indent) throws Exception {
TransformerFactory factory = TransformerFactory.newInstance(); // ← no hardening
factory.setAttribute(INDENT_NUMBER, indent);
Transformer transformer = factory.newTransformer();
// ...
transformer.transform(new StreamSource(new StringReader(xml)), // ← XXE
new StreamResult(out));
}
```
Same pattern — untrusted XML in via `StreamSource`, no `FEATURE_SECURE_PROCESSING` on the `TransformerFactory`.
`NoIndentNumberPrettyPrinter` (similar class in the same package) and `DocumentBuilderFactory` use in `docx/DocxReport.java:165` and `xslt/DefaultTransformerFactory.java:45` (in the FOP sandbox module) likely have the same shape — recommend reviewing all `DocumentBuilderFactory` / `TransformerFactory` constructions in the codebase.
Impact
The DOMUtils.load methods being public static + namespace-aware suggests they are entry points used wherever xdocreport reads document XML — a malicious .docx/.odt uploaded to any application using xdocreport for templating could trigger this.
Recommended fix
Apply JAXP hardening on all `DocumentBuilderFactory` and `TransformerFactory` instances at construction:
```java
// DocumentBuilderFactory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl\", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities\", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities\", false);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd\", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
// TransformerFactory
TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
```
A centralized `SafeXmlFactories` helper class in `fr.opensagres.xdocreport.core.utils` would let every call site share one hardened factory.
References:
Discovery attribution
Found by cognium-ai SAST during a top-100 Java repo sweep. Total 9 XXE findings in xdocreport — the 3+ above are confirmed by code read; the remaining sites likely follow the same pattern. Happy to review a patch.