From 76ed90157b9e8d9e9453a02495adde3144dddc3f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 28 Oct 2026 04:24:43 +0000 Subject: [PATCH] fix(parse): end raw-text tags on a solidus htmlparser2 only ends script, style, title, textarea, and xmp on ">" or whitespace. A " --- src/index.js | 12 ++++++++++++ test/index.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/index.js b/src/index.js index c38b0ae..ba19e63 100644 --- a/src/index.js +++ b/src/index.js @@ -13,6 +13,18 @@ const Tokenizer = require(join( const IN_TAG_NAME = 3 class ForeignTokenizer extends Tokenizer { + // htmlparser2 ends a raw-text end tag on ">" or whitespace only. The HTML + // tokenizer also ends it on "/", so "". + stateInSpecialTag (char) { + if (this.sequenceIndex === this.currentSequence.length && char === 47) { + return super.stateInSpecialTag(32) + } + return super.stateInSpecialTag(char) + } + stateBeforeSpecialS (char) { if (this.cbs.foreignContext[0] === true) { this.state = IN_TAG_NAME diff --git a/test/index.js b/test/index.js index 644862a..819da7d 100644 --- a/test/index.js +++ b/test/index.js @@ -244,6 +244,38 @@ test('annotation-xml with an html encoding keeps the script open', t => { } }) +test('a raw-text end tag may end with a solidus', t => { + t.deepEqual( + parseIframe(''), + [{ src: '/a.js' }, { src: '/b.js' }] + ) + t.deepEqual( + parseIframe(''), + [{ src: '/a.js' }, { src: '/b.js' }] + ) + t.deepEqual( + parseIframe( + '
' + ), + [{ src: '/a.js' }, { src: '/b.js' }] + ) + t.deepEqual( + parseIframe( + '' + ), + [{ src: '/a.js' }, { src: '/evil.js' }, { src: '/b.js' }] + ) + t.deepEqual(parseIframe(''), [ + { src: '/b.js' } + ]) + t.deepEqual( + parseIframe( + '' + ), + [{ src: '/a.js' }] + ) +}) + test('a script end tag may have whitespace before >', t => { for (const gap of [' ', '\n', '\t', '\r', '\f']) { t.deepEqual(