CVE-2026-24400

AssertJ XXE: When Test Code Bites Back

Alon Barad
Alon Barad
Software Engineer

Jan 26, 2026·6 min read·5 visits

Executive Summary (TL;DR)

AssertJ, the beloved fluent assertion library for Java, failed to disable DTD processing in its internal XML pretty-printer. If you pass a malicious XML string to `isXmlEqualTo()`, the library blindly resolves external entities. This allows attackers to read files (like `/etc/passwd`) or hit internal network endpoints, potentially compromising CI/CD pipelines or production systems where test utilities are improperly used.

A critical XML External Entity (XXE) vulnerability in AssertJ allows attackers to read local files or perform SSRF via malicious XML strings passed to assertion methods.

The Hook: Who Watches the Watchmen?

We tend to treat test code with a distinct lack of respect. It's the messy sibling of production code—often hastily written, rarely audited, and assumed to run in a "safe" environment. AssertJ is the go-to library for writing fluent assertions in Java. It makes your tests read like English. But here's the kicker: code is code, whether it lives in src/main or src/test. And vulnerabilities don't care about your package structure.

CVE-2026-24400 is a classic example of why "it's just a test tool" is a dangerous mindset. The vulnerability lies deep within org.assertj.core.util.xml.XmlStringPrettyFormatter, a utility class designed to simply make XML look nice when a test fails. The problem? To pretty-print XML, you have to parse it first. And if you parse XML in Java without explicitly telling the parser not to talk to strangers, you're going to have a bad time.

This isn't just about crashing a unit test. Modern CI/CD pipelines run tests automatically, often against data generated from external sources or integration points. If an attacker can influence the string being asserted—say, via a pull request validation or a mock data injection—they can turn your build agent into an accomplice, exfiltrating secrets or mapping your internal network.

The Flaw: The Ghost of Defaults Past

The root cause here is as old as Java itself: unsafe defaults in the Java API for XML Processing (JAXP). When you ask Java for a DocumentBuilderFactory, it hands you one that is eager to please. By default, it supports Document Type Definitions (DTDs) and External Entities. This is a feature from the 90s that has become the security nightmare of the 2020s.

The vulnerable class, XmlStringPrettyFormatter, used the method toXmlDocument(String) to ingest XML strings for formatting. It initialized the factory and immediately parsed the string. It didn't disable DTDs. It didn't disable external entity resolution. It effectively said, "Here is a complex data structure that can execute network requests or file reads; please process it fully."

This is the architectural equivalent of leaving your front door unlocked because you live in a gated community. You're relying on the environment (the assumption that input is safe) rather than the mechanism (the parser configuration) for security. When that environment assumption fails, the mechanism provides zero protection against XXE injection.

The Code: The Smoking Gun

Let's look at the crime scene. In versions prior to 3.27.7, the code was shockingly simple—and shockingly naive. Here is the vulnerable implementation of toXmlDocument:

private static Document toXmlDocument(String xmlString) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    // CRITICAL FLAW: No feature flags set to disable DTDs
    DocumentBuilder builder = factory.newDocumentBuilder();
    return builder.parse(new InputSource(new StringReader(xmlString)));
}

The fix, applied in commit 85ca7eb6609bb179c043b85ae7d290523b1ba79a, is a lesson in paranoia. The maintainers didn't just fix the bug; they scorched the earth. They explicitly disabled every single feature that could possibly be used for mischief.

private static Document toXmlDocument(String xmlString) throws Exception {
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    // THE FIX: Explicitly disable everything
    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);
    DocumentBuilder builder = factory.newDocumentBuilder();
    return builder.parse(new InputSource(new StringReader(xmlString)));
}

They also added a @Deprecated annotation to the entire class, effectively admitting, "We shouldn't be doing this parsing logic internally anyway."

The Exploit: Reading /etc/passwd via Unit Tests

Exploiting this requires an attacker to control the string passed to assertThat(actual).isXmlEqualTo(expected). While this sounds rare, consider a scenario where a test suite verifies the output of an XML API. If the attacker can influence the API response (e.g., via a stored XSS-like vector in a database that the API reads from), the test runner becomes the victim.

The exploit payload is standard XXE boilerplate. We define a DOCTYPE with a malicious ENTITY pointing to a local file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pwn [
  <!ENTITY file SYSTEM "file:///etc/passwd">
]>
<root>&file;</root>

When AssertJ runs isXmlEqualTo, it calls XmlStringPrettyFormatter to format the "actual" string for comparison or error reporting. The vulnerable DocumentBuilder sees the &file; entity, resolves it by reading the contents of /etc/passwd, and substitutes the content into the XML. If the assertion fails (which is likely, since the expected XML probably doesn't contain the contents of /etc/passwd), the error message will helpfully print the difference—dumping the file contents right into your CI/CD logs.

The Impact: Why You Should Care

Why is an XXE in a testing library rated High (CVSS 8.2)? Context is king. AssertJ is used heavily in enterprise Java environments. The impact generally falls into two buckets:

1. CI/CD Compromise: Build agents often hold the keys to the kingdom—AWS credentials, deployment keys, and signing certificates. An XXE can be used to perform Server-Side Request Forgery (SSRF) to hit the cloud metadata service (e.g., http://169.254.169.254/latest/meta-data/) and steal temporary credentials. If your test suite runs on a compromised input, your build pipeline is owned.

2. Production Misuse: Developers are creatures of habit. Sometimes, they use "test" libraries in production code because they like the syntax or need a quick utility. If XmlStringPrettyFormatter was used in a production web service to format logs or debug output, this becomes a remotely exploitable Remote Code Execution (via complex deserialization chains) or file disclosure vulnerability on the live server.

The Fix: Patch and Deprecate

The immediate remediation is to upgrade AssertJ to version 3.27.7. This version includes the hardened XML parser configuration. However, the maintainers have gone a step further: they are telling you to stop using isXmlEqualTo altogether.

> [!NOTE] > The XmlStringPrettyFormatter class and its associated methods are now deprecated. The AssertJ team recommends migrating to specialized XML testing libraries like XMLUnit for robust and secure XML comparisons.

If you cannot upgrade immediately, you must audit your code to ensure that no untrusted input reaches isXmlEqualTo. If you are using this method to validate user uploads or external webhooks in a test environment, you are playing Russian Roulette with your build infrastructure.

Fix Analysis (1)

Technical Appendix

CVSS Score
8.2/ 10
CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:L/SC:H/SI:N/SA:N

Affected Systems

Java applications using AssertJ < 3.27.7CI/CD Pipelines running tests with AssertJProduction systems improperly including test dependencies

Affected Versions Detail

Product
Affected Versions
Fixed Version
AssertJ Core
AssertJ
>= 1.4.0, < 3.27.73.27.7
AttributeDetail
CWE IDCWE-611 (XXE)
CVSS 4.08.2 (High)
Attack VectorLocal (Context Dependent)
ImpactHigh (Confidentiality)
Exploit StatusPoC Available
Patch Date2026-01-24
CWE-611
Improper Restriction of XML External Entity Reference

The software processes an XML document that can contain XML entities with URIs that resolve to documents outside of the intended sphere of control, causing the product to embed incorrect documents into its output.

Vulnerability Timeline

Fix committed by Stefano Cordio
2026-01-24
CVE-2026-24400 Published
2026-01-26
GHSA-rqfh-9r24-8c9r Published
2026-01-26

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.