If a scanner flags the xercesImpl Maven artifact in your build, you are looking at two separate problems that get conflated constantly: a short list of real parser CVEs fixed by upgrading to 2.12.2, and the XXE risk class, which no Xerces version fixes for you because external entity processing is a configuration decision your code makes. Apache Xerces is the grandfather of Java XML parsing, it is literally the code the JDK's built-in parser was forked from, and xerces:xercesImpl still arrives transitively through PDF, Office-document, and SVG libraries in thousands of builds that never asked for it. Sorting the upgrade problem from the configuration problem is the point of this guide.
What xercesImpl is and how it gets into your tree
The artifact:
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
Note the bare group ID xerces, a pre-reverse-DNS relic, like junit:junit, that marks how old this project is. Xerces2 Java implements SAX, DOM, and JAXP parsing, and version 2.12.2 (January 2022) is the newest release; the project moves at a geological pace because XML parsing is finished technology.
Almost nobody declares it directly anymore, because the JDK ships its own internal fork (com.sun.org.apache.xerces.internal.*). It arrives transitively: Apache POI's ooxml chain, PDFBox, Batik, older SOAP stacks, and enterprise products of a certain vintage all drag it in. Find your chain with:
mvn dependency:tree -Dincludes=xerces:xercesImpl
The transitive arrival matters operationally: when xercesImpl is on the classpath, JAXP's factory lookup can resolve to it instead of the JDK parser, silently changing which parser, and which defaults, your entire application uses. That is why an old xercesImpl deep in your tree is not just dead weight; it can be the parser actually handling your untrusted XML.
The real CVE list (and the upgrade that clears it)
Xerces-J's advisory history is short for a 25-year-old parser:
- CVE-2009-2625: malformed XML causing a parsing loop and denial of service, an old one, but IBM and other vendors were still shipping affected 2.9.x jars years later.
- CVE-2012-0881: the hash-collision DoS of that era, attacker-chosen element names degrading hash table performance.
- CVE-2013-4002: crafted documents driving the scanner into heavy CPU consumption.
- CVE-2022-23437: the current-era entry, a specially crafted payload sends versions through 2.12.1 into an infinite loop. Fixed in 2.12.2.
Pattern recognition: every one of these is denial of service through parser state, not code execution. The remediation is uniform and cheap, pin 2.12.2:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
</dependencies>
</dependencyManagement>
Since it is usually transitive, dependencyManagement (or a Gradle resolution strategy) is the right lever, it overrides whatever stale version your document library requested. An SCA tool such as Safeguard will tell you which of your services carry pre-2.12.2 copies and through which parent; the fix itself is a one-line pin because the 2.x line has been API-stable for two decades.
XXE: the vulnerability that is not in the changelog
Now the bigger risk, the one with no fixed-in version. XML External Entity injection (XXE) abuses a feature: DTDs can define entities that reference external resources, and a compliant parser resolves them. Feed such a document to a parser with entity processing enabled and it will read local files or make network requests on the attacker's behalf. Conceptually:
<?xml version="1.0"?>
<!DOCTYPE data [ <!ENTITY ext SYSTEM "file:///etc/hostname"> ]>
<data>&ext;</data>
If your service echoes any part of the parsed document back, the entity's contents come with it; even blind, entity resolution enables server-side request forgery and file-existence probing. This works on a fully patched xercesImpl 2.12.2 with default-ish settings, because resolving entities is what the spec says a parser does. That is why XXE lives in the OWASP canon as a configuration flaw (CWE-611), not a parser bug.
The hardening block to standardize on
For DOM parsing via JAXP, this is the configuration worth wrapping in a shared factory method and banning raw DocumentBuilderFactory.newInstance() in code review:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
// The single most effective line: no DOCTYPE, no XXE
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// Defense in depth if you must accept DTDs
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
Notes from the trenches:
- Disallowing DOCTYPE outright is the answer whenever your schema does not require DTDs, which for APIs is nearly always. It also kills the billion-laughs entity-expansion DoS in the same stroke.
- The same features apply to
SAXParserFactory;XMLInputFactory(StAX) uses its own properties (XMLInputFactory.SUPPORT_DTDset tofalse,IS_SUPPORTING_EXTERNAL_ENTITIESset tofalse). - Every parser entry point counts. Transformers (
TransformerFactory), validators, schema factories, and XML-consuming libraries (SOAP clients, RSS parsers, document converters) each construct parsers internally. The libraries that dragged xercesImpl into your build are exactly the ones parsing attacker-supplied files, ask what their parser settings are. - OWASP's XXE Prevention Cheat Sheet is the authoritative per-parser reference; encode it into a utility class once rather than relying on developers remembering feature URIs.
Configuration bugs like this are invisible to dependency scanning by definition, the jar version is fine, the settings are not. Probing your XML-accepting endpoints with DOCTYPE-bearing documents is a standard DAST check, and it is the verification layer that catches the parser entry point someone forgot to harden.
Do you even need xercesImpl anymore?
Worth asking during cleanup. The JDK's built-in parser handles standard SAX/DOM/StAX workloads, and modern JDKs expose JAXP limit properties (jdk.xml.*) that make hardening easier. If xercesImpl is in your tree only because a document library requested it a decade ago, test whether current versions of that library still need it; several dropped the explicit dependency. Removing it eliminates the factory-resolution ambiguity and one more artifact from your SBOM. If it must stay, pin 2.12.2, harden the factories, and move on, it is a well-behaved dependency once both problems are addressed.
FAQ
What is xercesImpl in Maven?
xerces:xercesImpl is Apache Xerces2, the veteran Java XML parser implementing SAX, DOM, and JAXP. It usually enters builds transitively through document-processing libraries (POI, PDFBox, Batik, SOAP stacks) rather than by direct declaration.
What CVEs affect xercesImpl and what version fixes them?
The list is all denial-of-service class: CVE-2009-2625, CVE-2012-0881, CVE-2013-4002, and most recently CVE-2022-23437, an infinite loop fixed in 2.12.2. Pinning 2.12.2 via dependencyManagement clears every published advisory.
Does upgrading xercesImpl fix XXE?
No. XXE is a configuration issue in whatever code constructs the parser: external entity resolution is enabled feature behavior, not a bug. Disallow DOCTYPE declarations (or disable external entities) on every DocumentBuilderFactory, SAXParserFactory, and XMLInputFactory that touches untrusted XML.
Why does my app use xercesImpl instead of the JDK parser?
JAXP factory lookup prefers implementations found on the classpath, so the presence of xercesImpl can silently take over XML parsing application-wide. That makes its version, and your hardening of it, matter even when your code never imports an Apache class.