From 48dd06583f1d10a785b0f04e0a806e3d3ac60150 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Thu, 9 Jul 2026 15:35:30 +0100 Subject: [PATCH] fix: reject numeric character references with trailing non-digit characters A numeric character reference must consist solely of digits (`&#[0-9]+;` or `&#x[0-9a-fA-F]+;`), but the reference was validated with parseInt(), which silently stops at the first invalid digit. As a result malformed references such as `Aa;`, `0f;`, and `Ag;` were accepted and resolved to their leading numeric portion (`A`, `0`, `A`) instead of being rejected as a well-formedness error. Validate the reference digits before resolving the code point. --- src/lib/Parser.ts | 3 ++- tests/lib/Parser.test.js | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib/Parser.ts b/src/lib/Parser.ts index 8f4c429..5df9805 100644 --- a/src/lib/Parser.ts +++ b/src/lib/Parser.ts @@ -609,7 +609,8 @@ export class Parser { ? parseInt(ref.slice(2), 16) // Hex codepoint. : parseInt(ref.slice(1), 10); // Decimal codepoint. - if (isNaN(codePoint)) { + if (isNaN(codePoint) + || !/^#(?:x[0-9A-Fa-f]+|[0-9]+)$/.test(ref)) { throw this.error('Invalid character reference'); } diff --git a/tests/lib/Parser.test.js b/tests/lib/Parser.test.js index d7f5f6e..0eca1a2 100644 --- a/tests/lib/Parser.test.js +++ b/tests/lib/Parser.test.js @@ -408,6 +408,17 @@ describe('Parser', () => { assert.strictEqual(parseXml(' ').root.children[0].text, '\r\n'); }); + // A numeric character reference must consist solely of digits, so a + // reference like `Aa;` or `Ag;` is malformed and must be rejected + // rather than silently truncated to the leading numeric portion. + // https://www.w3.org/TR/2008/REC-xml-20081126/#NT-CharRef + it('rejects a numeric character reference with trailing non-digit characters', () => { + assert.throws(() => parseXml('Aa;'), /Invalid character reference/); + assert.throws(() => parseXml('0f;'), /Invalid character reference/); + assert.throws(() => parseXml('Ag;'), /Invalid character reference/); + assert.throws(() => parseXml(''), /Invalid character reference/); + }); + it('handles many character references in a single attribute', () => { let { root } = parseXml(''); assert.strictEqual(root.attributes.b, "<".repeat(35));