diff --git a/spec/validator_spec.js b/spec/validator_spec.js index 0d07e227..e6d91292 100644 --- a/spec/validator_spec.js +++ b/spec/validator_spec.js @@ -2,7 +2,7 @@ import fs from "fs"; import path from "path"; -import {XMLValidator} from "../src/fxp.js"; +import {XMLParser, XMLValidator} from "../src/fxp.js"; import { fileURLToPath } from 'url'; import { dirname } from 'path'; @@ -405,6 +405,31 @@ describe("should not validate XML documents with multiple root nodes", () => { }); }); +describe("should validate only allowed content after the root element", () => { + const invalidContent = ['&', 'A', '', '']; + const roots = ['', '']; + + it('rejects references, CDATA and DOCTYPE in the epilog', () => { + for (const root of roots) for (const content of invalidContent) { + expect(XMLValidator.validate(`${root}${content}`)).not.toBe(true); + } + expect(XMLValidator.validate('')).not.toBe(true); + }); + + it('rejects the same content when parsing with validation enabled', () => { + const parser = new XMLParser(); + for (const root of roots) for (const content of invalidContent) { + expect(() => parser.parse(`${root}${content}`, true)).toThrow(); + } + }); + + it('allows whitespace, comments and processing instructions in the epilog', () => { + for (const root of roots) { + validate(`${root} \n`); + } + }); +}); + describe("should report correct line numbers for unclosed tags", () => { it('- child tag', () => diff --git a/src/validator.js b/src/validator.js index 6f9904f0..1a2be6c3 100644 --- a/src/validator.js +++ b/src/validator.js @@ -85,12 +85,18 @@ export function validate(xmlData, options) { i = result.index; if (attrStr[attrStr.length - 1] === '/') { + if (reachedRoot === true) { + return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i)); + } //self closing tag const attrStrStart = i - attrStr.length; attrStr = attrStr.substring(0, attrStr.length - 1); const isValid = validateAttributeString(attrStr, options); if (isValid === true) { tagFound = true; + if (tags.length === 0) { + 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 @@ -144,6 +150,9 @@ export function validate(xmlData, options) { for (i++; i < xmlData.length; i++) { if (xmlData[i] === '<') { if (xmlData[i + 1] === '!') { + if (reachedRoot === true && !xmlData.startsWith('