The npm xmldom package is deprecated: since version 0.7.0 the project publishes exclusively as @xmldom/xmldom, which means every security fix from the last several years has shipped to the scoped name while the unscoped xmldom sits frozen with known vulnerabilities. If your lockfile still resolves the old name, you are not running an old version of a maintained library — you are running a name that no longer receives patches at all. This post covers what happened, which advisories matter, and the three realistic migration paths.
How xmldom ended up with two names
xmldom is a pure-JavaScript implementation of the W3C DOM for XML: a DOMParser and XMLSerializer you can use in Node.js, where the browser's native DOMParser does not exist. It became the default answer for "parse XML on the server without native bindings" and spread through SOAP clients, SAML libraries, RSS readers, and Office-document tooling.
The maintainers lost the ability to publish to the original unscoped package name and moved the project to the @xmldom scope. The project's own release discussion is explicit: from 0.7.0 onward, releases go to @xmldom/xmldom and no longer to xmldom. npm shows the old package with a deprecation notice, but deprecation notices do not fail builds — installs proceed with a warning most CI logs scroll straight past. Years later, plenty of dependency trees still carry the dead name.
This situation is worth distinguishing from ordinary abandonment. The code is actively maintained; only the name you may be depending on is not. That makes it more fixable — and less excusable — than a genuinely orphaned dependency. Our post on abandoned open source project risks covers the harder case.
The advisory that defines the split: CVE-2022-39353
The clearest illustration of the two-name problem is CVE-2022-39353. xmldom parsed XML documents that are not well-formed because they contain multiple top-level elements, silently adding every root to the document's childNodes collection without reporting an error. That breaks the foundational assumption that an XML document has exactly one root.
Why is that a security issue rather than a parsing quirk? Because downstream code checks properties of "the root element" to make decisions. Think of a signed XML document where a validator inspects one element while a consumer reads another: an attacker who can smuggle a second top-level element gets two documents wearing one signature. Signature-wrapping attacks against SAML and other XML security protocols are built from exactly this kind of parser disagreement.
The fixed versions — @xmldom/xmldom@~0.7.7, @xmldom/xmldom@~0.8.4, or 0.9.0-beta.4 and later — exist only under the scoped name. There is no fixed unscoped xmldom. If the advisory applies to you, migration is the patch.
Finding the old name in your tree
Direct dependencies are the easy part. The old name usually survives transitively, held by an unmaintained SOAP or SAML wrapper:
npm ls xmldom
npm audit
If a transitive dependency insists on xmldom, you can redirect the resolution without forking, because @xmldom/xmldom kept API compatibility for the mainstream DOMParser/XMLSerializer surface:
{
"overrides": {
"xmldom": "npm:@xmldom/xmldom@^0.8.10"
}
}
Yarn users can express the same redirect with resolutions. Afterwards, verify with npm ls that nothing still resolves the unscoped name, and run the dependent's test suite — packages that reached into xmldom internals (undocumented properties, error message strings) occasionally notice the difference.
Migration option by option
Option 1: move to @xmldom/xmldom (default choice). One install and an import rename:
npm uninstall xmldom
npm install @xmldom/xmldom
// before
const { DOMParser } = require("xmldom");
// after
const { DOMParser } = require("@xmldom/xmldom");
Pick up 0.8.x or the 0.9 line, both of which contain the well-formedness fixes. This is the lowest-effort path and the right one if you need DOM semantics — traversal, namespaces, serialization.
Option 2: switch to a non-DOM parser. If you only read data out of XML, fast-xml-parser (XML to plain objects, actively maintained, validation options) or the streaming saxes are lighter and avoid DOM emulation entirely. This is a code change, not a swap, but it usually deletes code in the process.
Option 3: stop parsing XML in JavaScript where the platform can. In browser or edge runtimes with a native DOMParser, use it. Native parsers get security attention no userland reimplementation can match.
Whichever you choose, apply the standard XML hygiene: reject documents above a size cap before parsing, never resolve external entities or fetch DTDs (a class of attack covered in our guide to finding and fixing XXE vulnerabilities), and treat parser errors as hard failures rather than warnings — silent tolerance of malformed input is precisely what CVE-2022-39353 punished.
The general lesson: renamed packages are invisible EOL
The xmldom case belongs to a category that version-range tooling handles badly: the package is "up to date" at its final version, npm outdated shows nothing, and only the advisory database knows the name is dead. The same pattern applies to other rename-and-rescope events across the ecosystem. Two controls catch it:
- Fail CI on deprecation warnings for production dependencies. A one-line check (
npm ls --jsonpiped through a script that inspectsdeprecatedfields) converts the scrolled-past warning into a build gate. - Scan with a tool that tracks advisory metadata, not just versions. An SCA scanner such as Safeguard flags
xmldomas vulnerable-with-no-fixed-version and points the remediation at the scoped successor, which is the answer a raw version diff can never give you.
FAQ
Is xmldom still maintained?
The project is maintained, but only under the name @xmldom/xmldom. The unscoped xmldom package stopped receiving releases at the 0.6.x era and is formally deprecated on npm; all fixes since, including security fixes, ship to the scoped package.
What versions of @xmldom/xmldom fix CVE-2022-39353?
The advisory lists ~0.7.7, ~0.8.4, and 0.9.0-beta.4 or later. If you are adopting the package fresh, take the newest stable 0.8.x or the 0.9 line rather than the minimum patched version.
Can I fix a transitive xmldom dependency without waiting for upstream?
Yes. Use npm overrides (or Yarn resolutions) to alias xmldom to npm:@xmldom/xmldom at a fixed version. The scoped package preserved the common API surface, so most dependents work unchanged — but run their tests, since edge-case behavior around malformed documents intentionally became stricter.
Should new projects use @xmldom/xmldom or something else?
If you need actual DOM semantics in Node, @xmldom/xmldom is the reasonable choice. If you just need data out of XML documents, a dedicated parser like fast-xml-parser is simpler and faster, and in runtimes with a native DOMParser the platform implementation should win by default.