CVE-2026-83607: xmldom: Element name injection via createElement() bypasses requireWellFormed

Published Sep 1, 2026
·
Updated

Summary

Document.createElement() in @xmldom/xmldom accepts arbitrary strings as the tagName parameter with zero validation. The serializer emits the tag name verbatim into XML/HTML output. Critically, the requireWellFormed: true serializer option — the recommended mitigation from CVE-2026-41672, CVE-2026-41674, and CVE-2026-34601 — did NOT catch this, making it a bypass of the existing security controls.

An attacker who controls the element name string can inject arbitrary attributes (including event handlers) into the serialized output, leading to XSS when the output is consumed by a browser or downstream parser.

Details

Document.createElement() accepts any string as tagName and stores it directly on the element node without validation. When the document is later serialized via XMLSerializer.serializeToString(), the serializer emits the tagName verbatim into the output.

The XML specification requires element names to conform to the Name production. The existing createAttributeNS() and createElementNS() methods validate qualified names against an anchored name/QName pattern, but createElement() bypasses this entirely, and the requireWellFormed: true serializer path performed no element-name validation — rendering it ineffective against this vector.

Root Cause

1. createElement() stores the raw tagName string without any validation. 2. The serializer's requireWellFormed code path did not validate element names against the XML Name/QName production. 3. The serializer emits tagName directly into angle brackets: <${tagName}...>.

Proof of Concept

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

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

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

const output = serializer.serializeToString(doc, { requireWellFormed: true }); console.log(output); // <root><img src=x onerror="alert(1)"/></root> // // A browser parsing this HTML will execute alert(1). // requireWellFormed: true did NOT prevent the injection.

Impact

Applications that use @xmldom/xmldom to construct DOM trees and serialize them to XML/HTML are vulnerable to injection attacks if any part of an element name originates from user input. This includes:

- Cross-Site Scripting (XSS): Injecting event handler attributes (onerror, onclick, etc.) into HTML output consumed by browsers. - XML injection: Breaking XML document structure by injecting closing tags, new elements, or processing instructions through the element name. - Security control bypass: Applications that adopted requireWellFormed: true as a mitigation for CVE-2026-41672 / 41674 / 34601 remained vulnerable through this vector.

@xmldom/xmldom can also be used inside browsers, where it mirrors the DOM API. Unlike the browser's createElement(), which rejects an invalid name with InvalidCharacterError, xmldom accepts it — developers may assume the same safety and skip validation.

Fix Applied

⚠ Opt-in required. Protection is not automatic. Existing serialization calls remain vulnerable unless { requireWellFormed: true } is explicitly passed. Applications that serialize untrusted DOM content should audit all serializeToString() call sites and add it.

When { requireWellFormed: true } is passed, the serializer now validates each element's serialized qualified name against the XML QName production and throws InvalidStateError before emitting the start tag. This also covers the namespace-prefix sub-vector: an invalid prefix surfaces either in the element qualified name (PREFIX:local) or in a synthesized xmlns:PREFIX declaration, and both are QName-checked.

Fixed under requireWellFormed: true in @xmldom/xmldom 0.9.11 and 0.8.14. Default serialization is unchanged.

PoC — fixed path

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

const doc = new DOMImplementation().createDocument(null, 'root', null); doc.documentElement.appendChild(doc.createElement('img src=x onerror="alert(1)"'));

// Default (unchanged): verbatim — injection present console.log(new XMLSerializer().serializeToString(doc)); // <root><img src=x onerror="alert(1)"/></root>

// Opt-in guard: throws InvalidStateError before serializing try { new XMLSerializer().serializeToString(doc, { requireWellFormed: true }); } catch (e) { console.log(e.name, e.message); // InvalidStateError: The element name "img src=x onerror="alert(1)"" is not a valid XML QName }

Why the default stays verbatim

The W3C DOM Parsing and Serialization spec defines a require well-formed flag whose default value is false. With the flag unset, the serializer emits element names verbatim, matching the XMLSerializer behavior of Chrome, Firefox, and Safari. Unconditionally throwing would be a behavioral breaking change with no spec justification; the opt-in requireWellFormed: true flag lets applications that require injection safety enable strict mode without breaking existing code.

Residual limitation

createElement(tagName) does not validate tagName at creation time. Enforcing an InvalidCharacterError for invalid names unconditionally at creation time is a breaking change and is deferred to the next breaking release. When the default serialization path is used (without requireWellFormed: true), invalid element names are still emitted verbatim; applications that do not pass requireWellFormed: true remain exposed.

Creation-time validation is tracked in a public issue on the next breaking-release milestone (filed at publication — issue link to be added), targeting the next breaking release.

Other sources

xmldom is a pure JavaScript W3C standard-based (XML DOM Level 2 Core) DOMParser and XMLSerializer module. Prior to @xmldom/xmldom versions 0.8.14 and 0.9.11, and in xmldom version 0.6.0 and earlier, Document.createElement(tagName) stores an unvalidated element name and XMLSerializer.serializeToString() emits that name verbatim. The requireWellFormed: true path did not validate the element qualified name or synthesized xmlns:PREFIX declaration, so attacker-controlled tag names could inject attributes, elements, or processing instructions into serialized XML or HTML and could cause cross-site scripting when browser-consumed. The unchecked values violate the XML QName constraint, and default serialization and creation-time createElement() behavior remain permissive. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.

— NVD

xmldom: Element name injection via createElement() bypasses requireWellFormed

— Microsoft

Affected Software

6 affected componentsFixes available
npm/@xmldom/xmldom<0.8.14
npm/@xmldom/xmldom<0.9.11
npm/@xmldom/xmldom<=0.6.0
npm/xmldom<=0.6.0
npm/@xmldom/xmldom>=0.7.0<=0.8.13
0.8.14
npm/@xmldom/xmldom>=0.9.0<=0.9.10
0.9.11

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/@xmldom/xmldom to a version that resolves this vulnerability.

    Fixed in 0.8.14
  2. Upgrade

    Upgrade npm/@xmldom/xmldom to a version that resolves this vulnerability.

    Fixed in 0.9.11
  3. Upgrade

    Upgrade @xmldom/xmldom to a version that resolves this vulnerability.

    Fixed in 0.8.14
  4. Upgrade

    Upgrade @xmldom/xmldom to a version that resolves this vulnerability.

    Fixed in 0.9.11
  5. Configuration

    Pass { requireWellFormed: true } to serializer.serializeToString(...) when serializing any DOM content that may include attacker-controlled element/qualified names; in this mode the serializer validates each element’s serialized qualified name against the XML QName production and throws InvalidStateError before emitting the start tag.

    @xmldom/xmldom XMLSerializer requireWellFormed = true
  6. Compensating control

    If you must serialize untrusted DOM content with @xmldom/xmldom, audit all serializeToString() call sites and ensure the requireWellFormed option is explicitly passed; otherwise the serializer emits tagName verbatim and the element-name injection can lead to XSS when consumed by a browser or downstream parser.

Event History

Sep 1, 2026
CVE Published
via MITRE·02:23 PM
Data Sourced
via MITRE·02:23 PM
DescriptionWeakness
Data Sourced
via NVD·03:17 PM
DescriptionSeverityWeakness
Sep 6, 2026
Data Sourced
via Microsoft·10:09 PM
DescriptionSeverityWeakness
Sep 8, 2026
Advisory Published
via GitHub·08:31 PM
Data Sourced
via GitHub·08:31 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Which applications are most exposed to this issue?

Applications that pass attacker-controlled strings to Document.createElement() and later serialize the resulting DOM are exposed. Risk is highest when the serialized XML or HTML is consumed by a browser, where injected markup can lead to cross-site scripting.

2

Is the default behavior affected?

Yes. Element creation and default serialization remain permissive, and requireWellFormed: true did not validate the element qualified name or synthesized namespace declaration.

3

What attacker-controlled input is needed to exploit this?

An attacker needs control over a tagName passed to Document.createElement(). A malformed name can then be emitted verbatim by XMLSerializer.serializeToString(), allowing injection of attributes, elements, or processing instructions.

4

Which versions have a fix?

@xmldom/xmldom is fixed in versions 0.8.14 and 0.9.11. xmldom has no fixed version available; affected releases include 0.6.0 and earlier.

5

What can be done if upgrading is not immediately possible?

Do not pass untrusted values to Document.createElement(). Enforce XML QName validation on tag names before element creation, and avoid rendering serialized output in browsers when untrusted names may have entered the DOM.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203