CVE-2026-83605
xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.7
xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
CVE-2026-83605- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-91
- Updated
- 2026-11-02
EUVDEUVD-2026-69215
8.7
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
EUVD-2026-69215- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-4w3w-2rp5-g8jm
8.7
xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
Summary
Element.setAttribute() in @xmldom/xmldom bypasses attribute name validation by calling the private _createAttribute(name) method, which performs no validation. The public createAttribute() method correctly validates names against an anchored QName pattern, but setAttribute() never uses it. The serializer escapes attribute values but trusts attribute names, allowing an attacker to inject additional attributes (including event handlers) into serialized output. The requireWellFormed: true option did not catch this.
Details
Element.setAttribute(name, value) creates attribute nodes by calling the private _createAttribute(name) method, which performs no validation on the name parameter. In contrast, the public Document.createAttribute(name) method validates the name against the QName production before creating the attribute node.
The result is a two-tier validation system where the most commonly used API (setAttribute) takes the unvalidated path:
doc.createAttribute("bad name")— throwsINVALID_CHARACTER_ERR(correct).el.setAttribute("bad name", "value")— succeeds silently (vulnerable).
The serializer emits attribute names verbatim into the output. Because attribute values ARE escaped (quotes, ampersands, etc.), the injection must occur through the name. An attacker can terminate the current attribute and inject new ones by including quote and space characters in the attribute name.
Root Cause
setAttribute()calls_createAttribute()(private, no validation) instead ofcreateAttribute()(public, validates againstQName).- The serializer trusts attribute names and emits them unescaped.
- The serializer's
requireWellFormedcode path did not validate attribute names during serialization.
Proof of Concept
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
const serializer = new XMLSerializer();
const doc = impl.createDocument(null, 'root', null);
// The attribute name contains a closing quote, a space, and a new attribute
doc.documentElement.setAttribute('class="safe" onclick', 'alert(1)');
const output = serializer.serializeToString(doc, { requireWellFormed: true });
console.log(output);
// <root class="safe" onclick="alert(1)"/>
//
// The single setAttribute() call produced TWO attributes:
// 1. class="safe"
// 2. onclick="alert(1)"
//
// requireWellFormed: true did NOT prevent the injection.
Demonstrating the validation gap
// Public createAttribute correctly rejects invalid names:
try {
doc.createAttribute('class="safe" onclick');
} catch (e) {
console.log('createAttribute rejects:', e.message);
}
// But setAttribute (which uses _createAttribute) accepts the same input:
doc.documentElement.setAttribute('class="safe" onclick', 'alert(1)');
// No error thrown
Impact
Applications that use setAttribute() with any user-controlled portion of the attribute name are vulnerable to attribute injection attacks. This includes:
- Cross-Site Scripting (XSS): Injecting event handler attributes into HTML output consumed by browsers.
- Security attribute override: Overriding security-relevant attributes such as
integrity,nonce,sandbox, orContent-Security-Policymeta attributes. - Validation bypass: The public
createAttribute()API validates whilesetAttribute()does not, creating an inconsistent security boundary that developers cannot rely on. - requireWellFormed bypass: Applications that adopted
requireWellFormed: trueas a mitigation for prior CVEs remained vulnerable.
@xmldom/xmldom can also be used inside browsers, where it mirrors the DOM API. Unlike the browser's setAttribute(), which rejects an invalid attribute 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…
- Record
GHSA-4w3w-2rp5-g8jm- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-91
- Updated
- 2026-11-02
NVD
8.7
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
CVE-2026-83605- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-91
- Updated
- 2026-11-02
OSV
8.7
xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
CVE-2026-83605- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-91
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-83605
8.7
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
DEBIAN-CVE-2026-83605- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Updated
- 2026-11-02
Show 10 moreShow less
OSVGHSA-4w3w-2rp5-g8jm
8.7
xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
Summary
Element.setAttribute() in @xmldom/xmldom bypasses attribute name validation by calling the private _createAttribute(name) method, which performs no validation. The public createAttribute() method correctly validates names against an anchored QName pattern, but setAttribute() never uses it. The serializer escapes attribute values but trusts attribute names, allowing an attacker to inject additional attributes (including event handlers) into serialized output. The requireWellFormed: true option did not catch this.
Details
Element.setAttribute(name, value) creates attribute nodes by calling the private _createAttribute(name) method, which performs no validation on the name parameter. In contrast, the public Document.createAttribute(name) method validates the name against the QName production before creating the attribute node.
The result is a two-tier validation system where the most commonly used API (setAttribute) takes the unvalidated path:
doc.createAttribute("bad name")— throwsINVALID_CHARACTER_ERR(correct).el.setAttribute("bad name", "value")— succeeds silently (vulnerable).
The serializer emits attribute names verbatim into the output. Because attribute values ARE escaped (quotes, ampersands, etc.), the injection must occur through the name. An attacker can terminate the current attribute and inject new ones by including quote and space characters in the attribute name.
Root Cause
setAttribute()calls_createAttribute()(private, no validation) instead ofcreateAttribute()(public, validates againstQName).- The serializer trusts attribute names and emits them unescaped.
- The serializer's
requireWellFormedcode path did not validate attribute names during serialization.
Proof of Concept
const { DOMImplementation, XMLSerializer } = require('@xmldom/xmldom');
const impl = new DOMImplementation();
const serializer = new XMLSerializer();
const doc = impl.createDocument(null, 'root', null);
// The attribute name contains a closing quote, a space, and a new attribute
doc.documentElement.setAttribute('class="safe" onclick', 'alert(1)');
const output = serializer.serializeToString(doc, { requireWellFormed: true });
console.log(output);
// <root class="safe" onclick="alert(1)"/>
//
// The single setAttribute() call produced TWO attributes:
// 1. class="safe"
// 2. onclick="alert(1)"
//
// requireWellFormed: true did NOT prevent the injection.
Demonstrating the validation gap
// Public createAttribute correctly rejects invalid names:
try {
doc.createAttribute('class="safe" onclick');
} catch (e) {
console.log('createAttribute rejects:', e.message);
}
// But setAttribute (which uses _createAttribute) accepts the same input:
doc.documentElement.setAttribute('class="safe" onclick', 'alert(1)');
// No error thrown
Impact
Applications that use setAttribute() with any user-controlled portion of the attribute name are vulnerable to attribute injection attacks. This includes:
- Cross-Site Scripting (XSS): Injecting event handler attributes into HTML output consumed by browsers.
- Security attribute override: Overriding security-relevant attributes such as
integrity,nonce,sandbox, orContent-Security-Policymeta attributes. - Validation bypass: The public
createAttribute()API validates whilesetAttribute()does not, creating an inconsistent security boundary that developers cannot rely on. - requireWellFormed bypass: Applications that adopted
requireWellFormed: trueas a mitigation for prior CVEs remained vulnerable.
@xmldom/xmldom can also be used inside browsers, where it mirrors the DOM API. Unlike the browser's setAttribute(), which rejects an invalid attribute 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…
- Record
GHSA-4w3w-2rp5-g8jm- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-91
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-83605
8.7
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
UBUNTU-CVE-2026-83605- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
OSVAZL-99347 No score
CVE-2026-83605 affecting package python-tensorboard 2.16.2-6
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, Element.setAttribute() calls the private _createAttribute(name) path without validating the attribute name, while Document.createAttribute(name) validates against QName. XMLSerializer.serializeToString() emits attribute names verbatim, and requireWellFormed: true did not validate them, so a crafted name can terminate the intended attribute and inject additional attributes, including event handlers, into browser-consumed output; synthesized xmlns:PREFIX declarations expose the same unchecked-name boundary. This issue is fixed in @xmldom/xmldom versions 0.8.14 and 0.9.11; no fixed version is available for xmldom.
- Record
AZL-99347- Updated
- 2026-11-02
OSVCLSA-2026-1788980919 No score
TuxCare security update for xmldom (5 CVEs)
TuxCare rebuilt xmldom to address 5 CVEs. Fixed in 0.6.0-tuxcare.4.
- Record
CLSA-2026-1788980919- Updated
- 2026-11-02
OSVCLSA-2026-1790458285 No score
TuxCare security update for xmldom (1 CVE)
TuxCare rebuilt xmldom to address 1 CVE. Fixed in 0.1.31-tuxcare.6.
- Record
CLSA-2026-1790458285- Updated
- 2026-11-02
OSVCLSA-2026-1790608669 No score
TuxCare security update for @xmldom/xmldom (9 CVEs)
TuxCare rebuilt @xmldom/xmldom to address 9 CVEs. Fixed in 0.9.10-tuxcare.1.
- Record
CLSA-2026-1790608669- Updated
- 2026-11-02
OSVCLSA-2026-1790755546 No score
TuxCare security update for @xmldom/xmldom (14 CVEs)
TuxCare rebuilt @xmldom/xmldom to address 14 CVEs. Fixed in 0.7.13-tuxcare.1.
- Record
CLSA-2026-1790755546- Updated
- 2026-11-02
OSVCLSA-2026-1791987856 No score
TuxCare security update for @xmldom/xmldom (4 CVEs)
TuxCare rebuilt @xmldom/xmldom to address 4 CVEs. Fixed in 0.9.8-tuxcare.5.
- Record
CLSA-2026-1791987856- Updated
- 2026-11-02
OSVROOT-APP-NPM-CVE-2026-83605 No score
CVE-2026-83605 in @xmldom/xmldom - Patched by Root
Root has patched CVE-2026-83605 in the @xmldom/xmldom package for Root:npm. Multiple fixed versions available.
- Record
ROOT-APP-NPM-CVE-2026-83605- Updated
- 2026-11-02
OSVROOT-APP-NPM-GHSA-4w3w-2rp5-g8jm No score
GHSA-4w3w-2rp5-g8jm in @xmldom/xmldom - Patched by Root
Root has patched GHSA-4w3w-2rp5-g8jm in the @xmldom/xmldom package for Root:npm. Multiple fixed versions available.
- Record
ROOT-APP-NPM-GHSA-4w3w-2rp5-g8jm- Updated
- 2026-11-06
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
@xmldom/xmldomnpm |
0.9.0 to 0.9.10 | 0.9.11 |
@xmldom/xmldomnpm |
0.7.0 to 0.8.13 | 0.8.14 |
xmldomnpm |
through 0.6.0 | None known |
python-tensorboardAzure Linux:3 |
through 2.16.2-6 | None known |
xmldomTuxCare:npm From an advisory covering 5 CVEs |
0.6.0-tuxcare.0 up to 0.6.0-tuxcare.4, plus listed versions 0.6.00.6.0-tuxcare.10.6.0-tuxcare.20.6.0-tuxcare.3 |
0.6.0-tuxcare.4 |
xmldomTuxCare:npm |
0.1.31-tuxcare.0 up to 0.1.31-tuxcare.6, plus listed versions 0.1.310.1.31-tuxcare.10.1.31-tuxcare.20.1.31-tuxcare.30.1.31-tuxcare.40.1.31-tuxcare.5 |
0.1.31-tuxcare.6 |
@xmldom/xmldomTuxCare:npm From an advisory covering 9 CVEs |
0.9.10-tuxcare.0 up to 0.9.10-tuxcare.1, plus listed versions 0.9.10 |
0.9.10-tuxcare.1 |
@xmldom/xmldomTuxCare:npm From an advisory covering 14 CVEs |
0.7.13-tuxcare.0 up to 0.7.13-tuxcare.1, plus listed versions 0.7.13 |
0.7.13-tuxcare.1 |
@xmldom/xmldomTuxCare:npm From an advisory covering 4 CVEs |
0.9.8-tuxcare.0 up to 0.9.8-tuxcare.5, plus listed versions 0.9.80.9.8-tuxcare.10.9.8-tuxcare.20.9.8-tuxcare.30.9.8-tuxcare.4 |
0.9.8-tuxcare.5 |
node-xmldomDebian:12 |
All versions, plus listed versions 0.8.6-10.9.10-10.9.10-20.9.11-10.9.12-1 Show 5 moreShow less0.9.5-10.9.6-10.9.8-10.9.8-20.9.9-1 |
None known |
Show 12 moreShow less
| Package | Affected | Fixed |
|---|---|---|
node-xmldomDebian:13 |
All versions, plus listed versions 0.9.10-10.9.10-20.9.11-10.9.12-10.9.6-10.9.8-10.9.8-20.9.9-1 |
None known |
node-xmldomDebian:14 |
before 0.9.11-1, plus listed versions 0.9.10-10.9.10-20.9.6-10.9.8-10.9.8-20.9.9-1 |
0.9.11-1 |
@xmldom/xmldomnpm |
0.9.0 up to 0.9.11 | 0.9.11 |
@xmldom/xmldomnpm |
0.7.0 up to 0.8.14 | 0.8.14 |
@xmldom/xmldomRoot:npm |
before 0.8.13-aikido.1 | 0.8.13-aikido.10.7.13-aikido.50.8.13-aikido.2 |
@rootio/xmldom__xmldomRoot:npm |
before 0.8.13-root.io.1 | 0.8.13-root.io.10.7.13-root.io.50.8.13-root.io.2 |
@xmldom/xmldomRoot:npm |
before 0.7.13-aikido.5 | 0.7.13-aikido.50.7.5-aikido.10.7.13-aikido.40.7.5-aikido.2 |
@rootio/xmldom__xmldomRoot:npm |
before 0.7.13-root.io.5 | 0.7.13-root.io.50.7.5-root.io.10.7.13-root.io.40.7.5-root.io.2 |
node-xmldomUbuntu:20.04:LTS |
All versions, plus listed versions 0.1.27+ds-10.1.27+ds-1+deb10u2build0.20.04.1 |
None known |
node-xmldomUbuntu:22.04:LTS |
All versions, plus listed versions 0.5.0-10.7.3-10.7.5-10.7.5-1ubuntu0.22.04.1 |
None known |
node-xmldomUbuntu:24.04:LTS |
All versions, plus listed versions 0.8.6-1 |
None known |
node-xmldomUbuntu:26.04:LTS |
All versions, plus listed versions 0.9.6-10.9.8-10.9.8-2 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | OSV | |
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-83605 is a high vulnerability (CVSS 8.7) in @xmldom Xmldom, published September 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.61%, the 49th percentile. Fixed in @xmldom/xmldom 0.9.11.
References
github.com/xmldom/xmldom/security/advisories/GHSA-4w3w-2rp5-g8jm
github.com/xmldom/xmldom/pull/1043
github.com/xmldom/xmldom/pull/1050
github.com/xmldom/xmldom/commit/cba1321218b069182695813fa7565653708e172e
github.com/xmldom/xmldom/commit/d8212e632507eaf1d9f609657dd4c56abeb12d44
github.com/xmldom/xmldom/releases/tag/0.8.14
Show 8 moreShow less
github.com/xmldom/xmldom/releases/tag/0.9.11
nvd.nist.gov/vuln/detail/CVE-2026-83605
github.com/advisories/GHSA-4w3w-2rp5-g8jm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/83xxx/CVE-2026-83605.json
security-tracker.debian.org/tracker/CVE-2026-83605
github.com/xmldom/xmldom
ubuntu.com/security/CVE-2026-83605
cve.org/CVERecord?id=CVE-2026-83605
Left out here: the rest of 2 source descriptions. The JSON record has them.