highCVE-2026-83607Sep 8, 2026

CVE-2026-83607: @xmldom/xmldom Element Name Injection via createElement()

Rohit Hatagale
AI Security Researcher, SecureLayer7

Passing any string to createElement() in @xmldom/xmldom lets an attacker inject arbitrary attributes and event handlers into serialized XML or HTML output, bypassing the requireWellFormed: true…

Package@xmldom/xmldom
Ecosystemnpm
Affected>= 0.9.0, <= 0.9.10
Fixed in0.9.11
CVE-2026-83607: @xmldom/xmldom Element Name Injection via createElement()

The problem

Document.createElement() accepted any string as tagName and stored it on the element node with zero validation. The serializer then emitted that name verbatim inside angle brackets.

Critically, the requireWellFormed: true serializer option, which was the recommended mitigation for several prior xmldom CVEs, also did no element-name validation. An attacker who controls the tag name string can break out of the element name and inject arbitrary HTML attributes, including event handlers like onerror.

Proof of concept

A working proof-of-concept for CVE-2026-83607 in @xmldom/xmldom, with the exact payload below.

javascript
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');

const doc = new DOMImplementation().createDocument(null, 'root', null);

// Tag name contains injected attributes with an XSS payload
const el = doc.createElement('img src=x onerror="alert(1)"');
doc.documentElement.appendChild(el);

// requireWellFormed: true does NOT block injection in affected versions
const out = new XMLSerializer().serializeToString(doc, { requireWellFormed: true });
console.log(out);
// Output: <root><img src=x onerror="alert(1)"/></root>
// A browser parsing this as HTML executes alert(1).

The serializer builds start tags as <${tagName} ...> with no QName check on the element name. Spaces and equals signs in the tagName string are treated as attribute syntax by any downstream HTML parser, so the injected payload lands as real attributes.

The requireWellFormed: true code path only validated text content and comment data in affected versions. It had no guard on element or attribute names. The patch (PR #1043, commit cba1321) adds a QName regex check against the serialized element name inside the requireWellFormed branch and throws InvalidStateError before the start tag is emitted.

Root cause is CWE-91 (XML Injection): untrusted input reaches an output serializer without validation.

The fix

Upgrade to @xmldom/xmldom 0.9.11 (or 0.8.14 on the 0.8.x line). After upgrading, call serializeToString() with { requireWellFormed: true } on every call site that handles untrusted DOM content. Without that option the serializer still emits names verbatim (matching W3C spec default behavior), so auditing all serializeToString() call sites is required.

Creation-time validation in createElement() is deferred to the next breaking release.

Reported by @bhaswanthc, @jmestwa-coder.

References: [1][2][3][4][5][6]

Related research