From 7152af681ca2841a10edb584800c6c208702de7b Mon Sep 17 00:00:00 2001 From: Alexander Kireev Date: Fri, 19 Jun 2026 16:33:12 +0700 Subject: [PATCH] fix: reject self-closing tags as multiple root nodes in validator XMLValidator only detected multiple root elements when the roots had child content. A self-closing root element (e.g. ) bypassed the check because the self-closing branch never marked the document root as reached, so neither the duplicate-root nor the trailing-text guard fired. Per the XML 1.0 spec a well-formed document has exactly one root element, so a self-closing tag at depth 0 completes the root. Mark reachedRoot in the self-closing branch and reject a second root element there. --- spec/validator_spec.js | 24 ++++++++++++++++++++++++ src/validator.js | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/spec/validator_spec.js b/spec/validator_spec.js index d1cf43bc..b256a343 100644 --- a/spec/validator_spec.js +++ b/spec/validator_spec.js @@ -403,6 +403,30 @@ describe("should not validate XML documents with multiple root nodes", () => { InvalidXml: 'Multiple possible root nodes found.' }, 5); }); + + it('when a self closing root node is followed by another self closing node', () => { + validate(``, { + InvalidXml: 'Multiple possible root nodes found.' + }); + }); + + it('when a self closing root node is followed by a paired node', () => { + validate(``, { + InvalidXml: 'Multiple possible root nodes found.' + }); + }); + + it('when a paired root node is followed by a self closing node', () => { + validate(``, { + InvalidXml: 'Multiple possible root nodes found.' + }); + }); + + it('when a self closing root node is followed by trailing text', () => { + validate(`extra`, { + InvalidXml: 'Extra text at the end' + }); + }); }); describe("should report correct line numbers for unclosed tags", () => { diff --git a/src/validator.js b/src/validator.js index 277225c2..3e26a29a 100644 --- a/src/validator.js +++ b/src/validator.js @@ -91,6 +91,14 @@ export function validate(xmlData, options) { const isValid = validateAttributeString(attrStr, options); if (isValid === true) { tagFound = true; + //a self closing tag at the root level is a complete root element + if (tags.length === 0) { + //if the root level has already been reached, this is a second root + if (reachedRoot === true) { + return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, tagStartPos)); + } + reachedRoot = true; + } //continue; //text may presents after self closing tag } else { //the result from the nested function returns the position of the error within the attribute