From 4678121ef163048de2d3f63ec3561fe950a98b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Afonso=20Janu=C3=A1rio?= Date: Sat, 5 Sep 2026 19:59:09 +0100 Subject: [PATCH] fix: recognize lowercase doctype declaration when ignoreDeclaration is set The tag-type checks for DOCTYPE (in the parser and in the validator) only matched an uppercase D, so from an HTML source fell through to the generic tag handler and showed up in the output as a bogus '!doctype' node instead of being skipped like a normal would be. XML itself is case sensitive here, but a lot of real-world input handed to this parser is HTML rather than strict XML, and HTML's doctype keyword is case-insensitive by spec, so it's worth treating both the same way. Made the comparison case-insensitive in both spots and added a test covering the html-style lowercase form. --- spec/entities_spec.js | 14 ++++++++++++++ src/validator.js | 8 +------- src/xmlparser/DocTypeReader.js | 7 +------ src/xmlparser/OrderedObjParser.js | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/spec/entities_spec.js b/spec/entities_spec.js index ff231a86..14d94f7e 100644 --- a/spec/entities_spec.js +++ b/spec/entities_spec.js @@ -73,6 +73,20 @@ describe("XMLParser Entities", function () { expect(result).toEqual(expected); }); + it("should parse a lowercase (HTML-style) doctype the same as an uppercase one", function () { + const xmlData = `hi`; + const expected = { + "html": { + "body": "hi" + } + }; + + const parser = new XMLParser({ ignoreDeclaration: true }); + let result = parser.parse(xmlData, true); + + expect(result).toEqual(expected); + }); + it("should parse XML with DOCTYPE without internal DTD", function () { const xmlData = ` diff --git a/src/validator.js b/src/validator.js index 6f9904f0..01728b9b 100644 --- a/src/validator.js +++ b/src/validator.js @@ -229,13 +229,7 @@ function readCommentAndCDATA(xmlData, i) { } } else if ( xmlData.length > i + 8 && - xmlData[i + 1] === 'D' && - xmlData[i + 2] === 'O' && - xmlData[i + 3] === 'C' && - xmlData[i + 4] === 'T' && - xmlData[i + 5] === 'Y' && - xmlData[i + 6] === 'P' && - xmlData[i + 7] === 'E' + xmlData.substring(i + 1, i + 8).toUpperCase() === 'DOCTYPE' ) { let angleBracketsCount = 1; for (i += 8; i < xmlData.length; i++) { diff --git a/src/xmlparser/DocTypeReader.js b/src/xmlparser/DocTypeReader.js index d65f8c17..2c99f646 100644 --- a/src/xmlparser/DocTypeReader.js +++ b/src/xmlparser/DocTypeReader.js @@ -14,12 +14,7 @@ export default class DocTypeReader { const entities = Object.create(null); let entityCount = 0; - if (xmlData[i + 3] === 'O' && - xmlData[i + 4] === 'C' && - xmlData[i + 5] === 'T' && - xmlData[i + 6] === 'Y' && - xmlData[i + 7] === 'P' && - xmlData[i + 8] === 'E') { + if (xmlData.substring(i + 3, i + 9).toUpperCase() === 'OCTYPE') { i = i + 9; let angleBracketsCount = 1; let hasBody = false, comment = false; diff --git a/src/xmlparser/OrderedObjParser.js b/src/xmlparser/OrderedObjParser.js index 58c6a5f2..5c5fff30 100644 --- a/src/xmlparser/OrderedObjParser.js +++ b/src/xmlparser/OrderedObjParser.js @@ -389,7 +389,7 @@ const parseXml = function (xmlData) { } i = endIndex; } else if (c1 === 33 - && xmlData.charCodeAt(i + 2) === 68) { //'!D' + && (xmlData.charCodeAt(i + 2) === 68 || xmlData.charCodeAt(i + 2) === 100)) { //'!D' or '!d' if (this.doctypefound) throw new Error("Multiple DOCTYPE declarations found."); this.doctypefound = true; const result = docTypeReader.readDocType(xmlData, i);