chore(deps): update dependency @xmldom/xmldom to v0.9.12 [security] - #1152
Open
renovate[bot] wants to merge 1 commit into
Open
chore(deps): update dependency @xmldom/xmldom to v0.9.12 [security]#1152renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
Codecov Reportβ
All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1152 +/- ##
==========================================
- Coverage 63.91% 63.83% -0.09%
==========================================
Files 19 19
Lines 2425 2425
Branches 575 575
==========================================
- Hits 1550 1548 -2
- Misses 875 877 +2 β View full report in Codecov by Harness. π New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
0.9.10β0.9.12xmldom: XML fragment injection via invalid EntityReference.nodeName during requireWellFormed serialization
CVE-2026-83610 / GHSA-6gmq-8vp8-gcm6
More information
Details
Summary
An
EntityReferencenode can be created with an invalid, attacker-controlled name throughDocument.createEntityReference(name). When this node is serialized directly with:the invalid
nodeNameis emitted into the serialized XML fragment without validation or escaping.This can produce real XML markup in the serialized output. In the proof of concept below, the serialized fragment contains
<injected/>, and reparsing the fragment creates a realinjectedelement.Details
The issue appears to be in the serialization path for
ENTITY_REFERENCE_NODE.For several other node types,
requireWellFormed: trueperforms specific validation checks before serialization. For example, comments, processing instructions, document types, and some character data cases are checked before being emitted.However, for
ENTITY_REFERENCE_NODE, the serializer appears to emit the node name directly in entity reference form:As a result, if
nodeNamecontains characters that break out of the intended&name;structure, the serializer can emit additional XML markup.For example, an entity reference created with the name:
is serialized as:
When this fragment is later parsed in an XML context,
<injected/>becomes a real element.This is especially surprising when
{ requireWellFormed: true }is used, because applications may reasonably treat this mode as the stricter or safer XML serialization mode.Proof of Concept
Tested with:
Observed output:
Impact
An application that creates an
EntityReferencefrom attacker-controlled input and then serializes that node or XML fragment withrequireWellFormed: truemay produce XML containing attacker-controlled markup.The impact is limited by two observations:
EntityReferencenodes from ordinary XML entity references.EntityReferencenode as an element child is rejected with aHierarchyRequestError.The main affected scenario is applications that directly use
createEntityReference(name)and then serialize the resulting node or fragment.Fix Applied
Two complementary, non-breaking fixes.
(1)
document.createEntityReference(name)rejects an invalidNameat creation, closing the reachable creation vector by default β the opt-in serializer check alone cannot, since a laternodeNamemutation would bypass a creation-only guard.(2) Under
requireWellFormed, the serializer validates theEntityReferencenodeNameas a well-formed XMLNameand throwsInvalidStateErrorwhen it is not; a valid reference still serializes as&name;. Both ship on both maintained versions. TheEntityReference/createEntityReferencedocs note that underrequireWellFormedthenodeNameis validated as an XMLName, and that xmldom does not expand entities. See the XMLNameproduction.Proof of Concept - fixed path
Why the default stays verbatim
The creation-time anchor is applied by default, because it is classified non-breaking. The serializer check, by contrast, stays gated behind
{ requireWellFormed: true }: W3C DOM Parsing's require-well-formed flag defaults tofalse, and the browserXMLSerializeremits thenodeNameverbatim in that default mode, so unconditionally throwing for an ill-formedEntityReference.nodeNamewould be an unjustified breaking change β which is why the default serialization path stays verbatim.Residual limitation
The creation vector is closed by default β the non-breaking creation-time anchor β with no further deferred work. The residual is at serialization: the default path still emits an ill-formed
nodeNameverbatim, because the serializer check is opt-in via{ requireWellFormed: true }.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator
CVE-2026-83618 / GHSA-vr34-hp96-76pp
More information
Details
Summary
An embedded line terminator bypasses the
requireWellFormedserializer check for aDocumentType'spublicId and systemId. The check was added to fix GHSA-f6ww-3ggp-fr8h; an id whose first line is a
valid literal slips past it and is emitted verbatim into the
<!DOCTYPE β¦>declaration, so the markupafter the line terminator breaks out into the surrounding document. Callers who enabled
requireWellFormedto neutralize DocumentType injection remain exposed.Details
publicIdandsystemIdare stored as raw values including their surrounding quotes, and thePubidLiteral/SystemLiteralproductions include those quotes. The serializer validates them withg.PubidLiteral_match.test(publicId)andg.SystemLiteral_match.test(systemId), where both matchersare
reg('^', β¦, '$')and inherit themflag from xmldom's shared regexp builder. Underm,$matches at an interior line terminator, so a value such as
"valid pubid"\n"><!ENTITY β¦>satisfiesthe matcher on its first line (
"valid pubid"is a completePubidLiteral) and the whole value βincluding the post-newline breakout β is emitted after
PUBLIC/SYSTEM.Root Cause
mflag.^β¦$undermare line anchors, not string anchors..test()) accepts any string with oneconforming line, so a complete, valid literal on the first line passes even though a line terminator
and breakout markup follow.
PubidCharexcluding</>does not prevent it β the breakout isappended after the literal, not embedded inside it.
The triggering line terminators are the ECMAScript
LineTerminatorset: U+000A, U+000D, U+2028, U+2029.Affected Versions
Only
@xmldom/xmldom0.9.x is affected. The vulnerable matchers are built bylib/grammar.js'sm-flaggedreg()builder, and the DocTypepublicId/systemIdrequireWellFormedcheck thatconsumes them was introduced in 0.9.10 (the GHSA-f6ww-3ggp-fr8h fix); 0.9.10 and 0.9.11 carry it.
0.8.xperforms the samerequireWellFormedcheck with inline, non-mregular expressions and is notaffected. The unscoped
xmldompackage has nogrammar.jsand norequireWellFormedserializer, sothere is no check to bypass.
Proof of Concept
Impact
requireWellFormed: trueto neutralize DocumentType injection remain exposed.the public or system identifier.
Fix Applied
The anchored
PubidLiteral/SystemLiteralvalidators used by therequireWellFormedserializer no longer treat an interior line terminator as satisfying the
$anchor, so apublicIdor
systemIdcontaining any ECMAScriptLineTerminator(U+000A, U+000D, U+2028, U+2029) is rejectedwith
InvalidStateError. Valid single-line identifiers serialize unchanged, and the defaultserialization path is unaffected.
Proof of Concept - fixed path
Why the default stays verbatim
The W3C DOM Parsing "require well-formed" flag defaults to false, and a browser
XMLSerializeremitsthe DOCTYPE verbatim. Unconditionally throwing on a malformed
publicId/systemIdwould be anunjustified breaking change to the default path, so the fix tightens only the opt-in
requireWellFormedvalidator, matching browser and spec defaults.Residual limitation
The guarantee holds only for callers that pass
{ requireWellFormed: true }; the defaultserialization path still emits
publicId/systemIdverbatim.publicIdandsystemIdare notvalidated at creation (
createDocumentType) or on direct property assignment(
documentType.publicId = β¦) β the WHATWG DOM specification places no well-formedness constraint onthese fields at creation time, so the serializer is the spec-aligned enforcement point.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: HTML raw-text closing-tag case mismatch causes output amplification
CVE-2026-83612 / GHSA-6mj3-qw4j-hgrw
More information
Details
Summary
In HTML mode (
text/html), a raw-text element (script,style,textarea,title) whose closingtag differs in case from its opening tag (e.g.
</ScRiPt>for<script>) is mishandled by theparser, producing quadratic (O(nΒ²)) output growth β a small crafted document parses and serializes
into output orders of magnitude larger, exhausting CPU and memory. A modest input of tens of KB can
therefore cause a denial of service in any service that parses untrusted HTML with xmldom. Only HTML
mode is affected.
Details
The parser calls
parseHtmlSpecialContentfor each raw-text element in HTML mode, matched viaisHTMLRawTextElement/isHTMLEscapableRawTextElement(so all four types βscript,style,textarea,titleβ are in scope). It searches for the element's closing tag withsource.indexOf('</' + tagName + '>', elStartEnd), a byte-for-byte case-sensitive match. Amixed-case closing tag never matches, so the search returns
-1, and the followingsource.substring(elStartEnd + 1, -1)extracts text backwards from the start of the documentinstead of the element's content. The function then returns
-1to the parse loop, which cannotadvance normally and falls back to character-by-character reprocessing. Every raw-text element
re-captures all source text preceding it, so output grows as O(nΒ²) in the number of such elements.
Root Cause
lib/sax.js:549):source.indexOf('</' + tagName + '>', elStartEnd)does not fold case, contrary to the WHATWG HTML RAWTEXT end-tag-name rule.-1(lib/sax.js:550):source.substring(elStartEnd + 1, elEndStart)runs evenwhen
elEndStart === -1, extracting text backwards from position 0.lib/sax.js:556): the function returnselEndStart(-1), drivingrepeated character-by-character fallback in the parse loop.
Affected Versions
Only the
0.9.xline is affected β the amplification was introduced in0.9.0-beta.1whenparseHtmlSpecialContentwas refactored, and remains through0.9.11. The0.8.xline is notaffected: its older
parseHtmlSpecialContentdoes not amplify, despite sharing the samecase-sensitive
indexOf.Proof of Concept
Output size grows quadratically with the number of case-mismatched raw-text elements:
Proof of Concept from @βKarimTantawey (tested with
script); the same amplification occurs forstyle,textarea, andtitle.Impact
Small attacker payloads can force disproportionate CPU and memory usage in services that
parse and serialize untrusted HTML via xmldom. The quadratic growth means a modest-sized input
(tens of kilobytes) can produce output in the tens or hundreds of megabytes, potentially
exhausting memory or causing timeouts.
The attack only requires HTML mode (
text/htmlMIME type) and mixed-case closing tags forany of the four raw-text element types. No special configuration or error handler setup is needed.
Severity note
The CVSS 4.0 vector scores availability only (
VA:H, withVC:N/VI:N): the flaw neither disclosesnor corrupts data, but a small untrusted HTML input (tens of KB) can force output and memory in the
tens to hundreds of MB, enough to exhaust a service's heap or stall its event loop. It is reachable
with no authentication, configuration, or error-handler setup β only that the application parses
untrusted
text/htmland serializes the result.Fix Applied
The raw-text closing tag is now matched case-insensitively in HTML raw-text mode (per the WHATWG HTML
RAWTEXT end-tag rule),
and a missing closing tag is handled explicitly, removing the quadratic output amplification. Output
for well-formed input is unchanged. Non-breaking; 0.9.x-only.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: Quadratic-time attribute deduplication
CVE-2026-83613 / GHSA-8344-3jmq-59r6
More information
Details
Summary
xmldom builds the attribute collection of every parsed element by inserting attributes one at a
time into a DOM
NamedNodeMap. Each insertion first performs a linear scan of allalready-inserted attributes to enforce the DOM uniqueness rule (no two attributes with the same
qualified name / namespace+local-name). Parsing an element that carries
Mdistinct attributestherefore costs
1 + 2 + β¦ + M = O(MΒ²)comparisons.Because the trigger is simply "one element with many attributes", the attack payload is a
fully well-formed XML document. No malformed markup, no error recovery, and no non-default
parser options are involved β parsing completes silently with zero
warning/error/fatalErrorevents. An attacker who can submit a modest, highly compressible document (a single element with
tens of thousands of attributes, ~340 KB uncompressed) can consume seconds of single-threaded CPU
per request, enabling an unauthenticated denial of service.
This is distinct from the known quadratic-memory namespace-map issue: it burns CPU and it
does not require any namespace declarations or nesting.
Details
The DOM content handler adds each attribute of a starting element by calling
el.setAttributeNode(attr)in a loop:https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom-parser.js#L370-L387
setAttributeNodedelegates toNamedNodeMap.setNamedItem, which callsgetNamedItemNSto lookfor an existing attribute with the same namespace URI and local name before appending:
https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L612-L623
getNamedItemNSwalks the whole list on every call:https://github.com/xmldom/xmldom/blob/bb7a085dc5ba1eea3212388509b97bb4b4af32b9/lib/dom.js#L702-L715
For the i-th attribute the scan visits
i-1entries, so insertingMdistinct attributes performsΞ(MΒ²)comparisons. There is no hash index or set keyed by name; the map is a plainarray-backed structure.
The same structure exists on 0.8.x. There
setNamedItemdedups viagetNamedItem(attr.nodeName)instead ofgetNamedItemNS, but that method is likewise a full linearscan, so the complexity is identical:
startElementloop /setAttributeNode:https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom-parser.js#L159-L176
setNamedItemβ lineargetNamedItem:https://github.com/xmldom/xmldom/blob/e5c14802592685bb872c042c54c3f73758875c85/lib/dom.js#L286-L308
The linear-scan
NamedNodeMappredates the@xmldom/xmldomfork and is present unchanged in theunscoped
xmldompackage back to its earliest published release. Inxmldom@0.1.0, parsing alreadyinserts each attribute one at a time (
DOMHandler.startElementloops callingsetAttributeNSβsetAttributeNodeβNamedNodeMap.setNamedItem), andsetNamedItemdedups bycalling
getNamedItemNS, which is a full linearwhile (i--)scan of the already-insertedattributes β the identical
O(MΒ²)structure. The whole unscoped line (0.1.0β¦0.6.0) istherefore affected; the earliest published tag (
0.1.0) was verified to contain the per-insertlinear dedup scan.
Proof of Concept
A single well-formed element with
Mdistinct attributes. No malformed markup and no options:Measured with Node.js v18.20.8 (wall-clock; absolute numbers vary by host, the scaling is the
load-bearing fact):
@xmldom/xmldom0.9.10:@xmldom/xmldom0.8.13:xmldom(unscoped) 0.6.0: 4000 β 28.2 ms, 8000 β 131.8 ms, 16000 β 545.2 ms (β Γ4 per doubling).Time grows β Γ4 per doubling of
Mβ quadratic. About 340 KB of well-formed input costs ~1.6β2.1 sof single-threaded CPU, and it keeps scaling: doubling the attribute count quadruples the cost.
The document is trivially generated and compresses to a few kilobytes on the wire.
Impact
Unauthenticated, remotely triggerable denial of service against any service that parses
attacker-influenced XML/HTML with xmldom. A single request holds one event-loop thread for seconds;
a handful of concurrent requests can saturate CPU and stall the process. Because the payload is a
plain well-formed document (one element, many attributes), it passes any "must be well-formed" gate
and reaches the parser before any application-level validation (e.g. schema checks or signature
verification) can run. The payload is highly compressible, so it is effective over compressed
transports.
Fix Applied
Replaced the per-insert linear duplicate scan on the parse-time dedup path with a name-keyed
index, so de-duplicating an element's attributes during parse is O(M) instead of O(MΒ²) β a
well-formed-but-hostile attribute list can no longer wedge the parse. Behavior-preserving: attribute
order and duplicate resolution (last value wins, first position kept) are byte-identical. Non-breaking
and independent of
requireWellFormed; ships on both maintained versions.Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
CVE-2026-83605 / GHSA-4w3w-2rp5-g8jm
More information
Details
Summary
Element.setAttribute()in@xmldom/xmldombypasses attribute name validation by calling the private_createAttribute(name)method, which performs no validation. The publiccreateAttribute()method correctly validates names against an anchoredQNamepattern, butsetAttribute()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. TherequireWellFormed: trueoption did not catch this.Details
Element.setAttribute(name, value)creates attribute nodes by calling the private_createAttribute(name)method, which performs no validation on thenameparameter. In contrast, the publicDocument.createAttribute(name)method validates the name against theQNameproduction 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).requireWellFormedcode path did not validate attribute names during serialization.Proof of Concept
Demonstrating the validation gap
Impact
Applications that use
setAttribute()with any user-controlled portion of the attribute name are vulnerable to attribute injection attacks. This includes:integrity,nonce,sandbox, orContent-Security-Policymeta attributes.createAttribute()API validates whilesetAttribute()does not, creating an inconsistent security boundary that developers cannot rely on.requireWellFormed: trueas a mitigation for prior CVEs remained vulnerable.@xmldom/xmldomcan also be used inside browsers, where it mirrors the DOM API. Unlike the browser'ssetAttribute(), which rejects an invalid attribute name withInvalidCharacterError, xmldom accepts it β developers may assume the same safety and skip validation.Fix Applied
When
{ requireWellFormed: true }is passed, the serializer now validates each serialized attribute's qualified name against the XMLQNameproduction and throwsInvalidStateErrorbefore emitting it. This covers ordinary attribute names and synthesizedxmlns:PREFIXnamespace declarations (the namespace-prefix sub-vector).Fixed under
requireWellFormed: truein@xmldom/xmldom0.9.11 and 0.8.14. Default serialization is unchanged.PoC β fixed path
Why the default stays verbatim
The W3C DOM Parsing and Serialization spec defines a
require well-formedflag whose default value isfalse. With the flag unset, the serializer emits attribute names verbatim, matching theXMLSerializerbehavior of Chrome, Firefox, and Safari. Unconditionally throwing would be a behavioral breaking change with no spec justification; the opt-inrequireWellFormed: trueflag lets applications that require injection safety enable strict mode without breaking existing code.Residual limitation
setAttribute(name, value)does not validatenameat creation time (unlike the publiccreateAttribute(), which already does). MakingsetAttribute()reject invalid names unconditionally is a breaking change and is deferred to the next breaking release. When the default serialization path is used (withoutrequireWellFormed: true), attribute names set viasetAttribute()are still emitted verbatim; applications that do not passrequireWellFormed: trueremain 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.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: DocType
nameInjection Bypasses requireWellFormedCVE-2026-83608 / GHSA-27p8-2357-5qqv
More information
Details
Summary
The
@xmldom/xmldomserializer emitsDocumentType.nameverbatim into the<!DOCTYPE β¦>declaration with no well-formedness guard. GHSA-f6ww-3ggp-fr8h(CVE-2026-41674) hardened the serializer's
requireWellFormedpath for aDocumentType's sibling fields β
publicId,systemId, andinternalSubsetβbut it did not add any check for
name. A>(or whitespace) in the nameterminates the doctype declaration early, letting the remaining characters
become sibling markup in the serialized output.
Because
requireWellFormed: trueβ the recommended mitigation for the priorxmldom injection CVEs β performs no validation on the DocType
name, this is abypass of that control, in the same family as the open element-name
(GHSA-w2rr-34g9-rvrj) and attribute-name (GHSA-4w3w-2rp5-g8jm) name-injection advisories.
Details
The serializer's
DOCUMENT_TYPE_NODEcase runs therequireWellFormedblockonly against
publicId,systemId, andinternalSubset, then pushesn.namedirectly into the buffer between the<!DOCTYPEprefix and theclosing
>:bb7a085):serializer DocType case,
lib/dom.js#L3256-L3283β the
requireWellFormedblock (#L3259-L3269)validates
publicId/systemId/internalSubsetbut notname, which isemitted verbatim at #L3270.
e5c1480):serializer DocType case,
lib/dom.js#L1914-L1946β same structure;
nameis emitted verbatim at #L1928.xmldom(v0.6.0,c80a161):lib/dom.js#L1105emits
node.nameverbatim; this line predatesrequireWellFormed, so thereis no well-formedness path at all.
Enabling write paths
DocumentType.nameis a plain, writable own-property, so the enabling vectordiffers by line:
createDocumentType()validates the name viavalidateQualifiedName(lib/dom.js#L925-L936,validation at #L926),
so the deliverable vector is a direct property write
(
dt.name = 'html><script>β¦') to the unguarded own-property.createDocumentType()does not validate the name(
lib/dom.js#L456-L464),so the malicious name is reachable directly through
createDocumentType()aswell as via direct property write.
xmldom(<= 0.6.0) βcreateDocumentType()does not validatethe name (
lib/dom.js#L286),same as 0.8.x.
This is the same structural root cause the sibling name-injection advisories
share: the serializer's
requireWellFormedpath validates content delimitersbut no name field, and every name-like field is a plain writable property, so
mutation / direct property-write bypasses any creation-time check.
Root Cause
requireWellFormedDocType block checkspublicId,systemId, andinternalSubset(the fields hardened by GHSA-f6ww-3ggp-fr8h)but has no check for
name.DocumentType.nameis a plain writable own-property; on 0.8.x and theunscoped package
createDocumentType()does not validate it either.namedirectly between the doctype delimiters:<!DOCTYPE ${name}β¦>.Proof of Concept
Run against
@xmldom/xmldomv0.9.10 (commitbb7a085):Confirmed runtime behavior:
createDocumentType()rejects the malicious name at creation(
InvalidCharacterError); a direct write todt.namebypasses that, andserializeToString(β¦, { requireWellFormed: true })emits the breakout with noexception.
<script>is a real second top-level element originating entirely from theDocType name: the parser rejects it with
HierarchyRequestError: Only one element can be added and only after doctype.A comment-injection variant (
dt.name = 'html><!--INJECTED--', output<!DOCTYPE html><!--INJECTED--><r/>) re-parses cleanly and the injectedcomment node is enumerable, confirming the injected node is structurally live.
e5c1480) βcreateDocumentType('html><script>β¦', '', '')accepts the malicious name directly (no creation-time validation), and
serializeToString(doc, null, null, { requireWellFormed: true })produces<!DOCTYPE html><script>alert(1)</script><r/>with no exception.A browser reproduction does not apply: browsers keep
DocumentType.namereadonly, so the direct-write vector cannot be reproduced in a browser DOM.The injection is specific to xmldom exposing
nameas writable and serializingit without a guard.
Impact
Applications that build a
DocumentTypenode with an attacker-influencednameβ via direct property write on any affected line, or viacreateDocumentType()on 0.8.x and the unscoped package β and serialize thedocument are vulnerable to XML/markup injection:
<!DOCTYPE β¦>declarationto inject arbitrary sibling elements, comments, or additional markup into the
output.
by a browser-based XML parser, an injected
<script>element (in the XHTMLnamespace) executes.
requireWellFormed: trueas a mitigation for the prior injection CVEs(including the sibling DocType fields fixed by GHSA-f6ww-3ggp-fr8h) remain
vulnerable through the DocType
name.Fix Applied
Under
requireWellFormed, the serializer validates the DocTypenameas a well-formed XMLNameand throwsInvalidStateErrorwhen it is not β matching the siblingpublicId/systemId/internalSubsetchecks. Non-breaking and opt-in; ships on both maintained versions. Nocreation-time change is made: 0.9.x already validates the name at
createDocumentType, and the0.8.x/unscoped creation gap cannot be closed without a breaking change, so it is left
unfixed. See the XML
Nameproduction.Proof of Concept - fixed path
Why the default stays verbatim
W3C DOM Parsing's require-well-formed flag defaults to false, and the browser
XMLSerializeremits the name verbatim in that default mode. Unconditionallythrowing would be an unjustified breaking change against that specified default,
so the guard is opt-in behind
{ requireWellFormed: true }.Residual limitation
The default serialization path still emits the ill-formed DocType
nameverbatim; protection applies only when
requireWellFormed: trueis passed. Nocreation-time validation is added for the DocType
name: 0.9.x already validatesat
createDocumentType, and the 0.8.x/unscoped creation gap is left unfixed β itcannot be closed without a breaking change.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: Creation-time XML Name/QName validation is bypassable via an embedded line terminator, allowing injection on the default serialization path
CVE-2026-83609 / GHSA-3px3-54cx-rmw9
More information
Details
Summary
An embedded line terminator bypasses xmldom's always-on, WHATWG-mandated creation-time name
validation.
createElementNS,createAttributeNS,createDocumentType, andcreateAttributeshouldreject a malformed qualified name with
InvalidCharacterError, but a name whose first line iswell-formed slips through and enters the DOM. On serialization it is emitted verbatim, so the
characters after the line terminator inject markup into the output. The injection reaches the default
serialization path, and enabling
requireWellFormeddoes not prevent it.Details
createElementNS,createAttributeNS, andcreateDocumentTyperoute throughvalidateQualifiedName,and
createAttributeperforms the analogous check; each validates the name withg.QName_exact.test(name).QName_exact = reg('^', QName, '$')inherits themflag from xmldom'sshared regexp builder, so the matcher accepts any name whose first line is a valid
QNameand leavesthe remaining lines unconstrained (see Root Cause).
Root Cause
mflag.^β¦$undermare line anchors, not string anchors.validateQualifiedName/createAttributevalidate with.test()against such a production, so aline terminator followed by breakout markup passes and the malformed name is stored.
The triggering line terminators are the ECMAScript
LineTerminatorset: U+000A, U+000D, U+2028, U+2029.Proof of Concept
Impact
InvalidCharacterError): amalformed name the standard requires be rejected at creation is instead admitted to the DOM.
create*APIs to reject malformednames (the standard behavior) as a trust boundary is exposed; where the serialized output reaches an
HTML context, downstream XSS.
path and
requireWellFormed: trueβ the strict serializer shares the samem-flagged blind spot(the subject of the sibling serializer advisories) β so the bypassed creation-time check was the only
layer that could have stopped it. Demonstrated for all four
create*sites inpoc_creation_strict_serialization_bypass.cjs.Fix Applied
createElementNS,createAttributeNS,createDocumentType, andcreateAttributenow reject a namecontaining a line terminator with
InvalidCharacterErrorβ the same result they already give forother malformed names β because name validation now applies to the whole string. The
requireWellFormedserializer's name checks are corrected by the same change. The fix isnon-breaking: such a name was already invalid, and no previously-accepted well-formed name is affected.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
xmldom: Parser silently accepts a not-well-formed end tag whose name is followed by a line break and trailing content
CVE-2026-83611 / GHSA-6h8r-xr42-gp59
More information
Details
Summary
xmldom's parser silently accepts a not-well-formed end tag whose valid name is followed by
trailing content β e.g.
</aβjunk>. The element is closed, the trailing content is discarded, and noerror is reported, even though the XML end-tag production allows only optional whitespace after the
name and both Chromium and Firefox reject such input as
application/xml. An application that relieson xmldom to reject not-well-formed input therefore receives a false "valid" result for a document the
specification and browsers consider malformed.
Details
Across every affected version, an end tag whose valid
Nameis followed by trailing content before>is silently accepted: the element is closed, the residue is dropped, and no error is reported. Howmuch