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));