From 085e21b5da05414efefa932570e7201a7c70e5b2 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Sun, 24 Mar 2024 17:59:46 -0400 Subject: [PATCH] fix: maxExpand limit with Unicode sub/superscripts * Prevent recursive parser calls from over-expansion * Use this.subparse instead of a recursive parser. * Add maxExpand tests * Lint fix --------- Co-authored-by: Ron Kok --- src/Parser.js | 7 ++++--- test/katex-spec.js | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/Parser.js b/src/Parser.js index 936246e789..0e002b3b8f 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -405,19 +405,20 @@ export default class Parser { // We treat these similarly to the unicode-math package. // So we render a string of Unicode (sub|super)scripts the // same as a (sub|super)script of regular characters. - let str = uSubsAndSups[lex.text]; const isSub = unicodeSubRegEx.test(lex.text); + const subsupTokens = []; + subsupTokens.push(new Token(uSubsAndSups[lex.text])); this.consume(); // Continue fetching tokens to fill out the string. while (true) { const token = this.fetch().text; if (!(uSubsAndSups[token])) { break; } if (unicodeSubRegEx.test(token) !== isSub) { break; } + subsupTokens.unshift(new Token(uSubsAndSups[token])); this.consume(); - str += uSubsAndSups[token]; } // Now create a (sub|super)script. - const body = (new Parser(str, this.settings)).parse(); + const body = this.subparse(subsupTokens); if (isSub) { subscript = {type: "ordgroup", mode: "math", body}; } else { diff --git a/test/katex-spec.js b/test/katex-spec.js index bd1380f14d..89cb389a2b 100644 --- a/test/katex-spec.js +++ b/test/katex-spec.js @@ -4002,6 +4002,29 @@ describe("The maxExpand setting", () => { expect`\edef0{x}\edef0{00}\edef0{00}\edef0{00}\edef0{00}`.not.toParse( new Settings({maxExpand: 10})); }); + + const exp32 = r` + \def\a#1{\b{#1}\b{#1}} + \def\b#1{\c{#1}\c{#1}} + \def\c#1{\d{#1}\d{#1}} + \def\d#1{\e{#1}\e{#1}} + \def\e#1{\f{#1}\f{#1}} + \def\f#1{#1} + `; + + it("should count correctly", () => { + const example = exp32 + r`\a{1}`; + const count = 1 + 2 + 4 + 8 + 16 + 32; + expect(example).toParse(new Settings({maxExpand: count})); + expect(example).not.toParse(new Settings({maxExpand: count - 1})); + }); + + it("should count correctly with Unicode sub/superscripts", () => { + const example = exp32 + r`\def+{\a{1}}x⁺x⁺x⁺x⁺`; + const count = (1 + 2 + 4 + 8 + 16 + 32) * 4 + 4; + expect(example).toParse(new Settings({maxExpand: count})); + expect(example).not.toParse(new Settings({maxExpand: count - 1})); + }); }); describe("The \\mathchoice function", function() {