From 7a77fa4f645b92f97d55cbb7457bbc01064ee6e0 Mon Sep 17 00:00:00 2001 From: Yang Jun Date: Mon, 11 May 2026 23:25:31 +0800 Subject: [PATCH] refactor(strip_html): drop position cache, delete dead blocks from Set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once `indexOf(closer, X)` returns -1, all subsequent searches (with monotonically increasing start) also return -1. So tracking absence is enough; storing positions is unnecessary. Make `blocks` a Set and delete a kind once its closer is known absent — no parallel `dead` bookkeeping. Use Jest's per-test timeout for the ReDoS regressions instead of manual Date.now() bookkeeping. Co-authored-by: Cursor --- src/filters/html.ts | 19 +++++++------------ test/integration/liquid/dos.spec.ts | 22 +++++++--------------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/filters/html.ts b/src/filters/html.ts index fb8096aef..e59adfd58 100644 --- a/src/filters/html.ts +++ b/src/filters/html.ts @@ -42,16 +42,11 @@ export function newline_to_br (this: FilterImpl, v: string) { return str.replace(/\r?\n/gm, '
\n') } -// '], [''], ['']] - const closes = [0, 0, 0] + const blocks = new Set<[string, string]>([[''], [''], ['']]) let out = '' let i = 0 while (i < str.length) { @@ -59,12 +54,12 @@ export function strip_html (this: FilterImpl, v: string) { if (lt < 0) return out + str.slice(i) out += str.slice(i, lt) let end = -1 - for (let k = 0; k < blocks.length; k++) { - const [opener, closer] = blocks[k] + for (const block of blocks) { + const [opener, closer] = block if (!str.startsWith(opener, lt)) continue - const from = lt + opener.length - if (closes[k] >= 0 && closes[k] < from) closes[k] = str.indexOf(closer, from) - if (closes[k] >= 0) end = closes[k] + closer.length + const e = str.indexOf(closer, lt + opener.length) + if (e < 0) blocks.delete(block) + else end = e + closer.length break } if (end < 0) end = str.indexOf('>', lt + 1) + 1 diff --git a/test/integration/liquid/dos.spec.ts b/test/integration/liquid/dos.spec.ts index 4b6cfd0df..85783564e 100644 --- a/test/integration/liquid/dos.spec.ts +++ b/test/integration/liquid/dos.spec.ts @@ -88,29 +88,21 @@ describe('DoS related', function () { describe('strip_html ReDoS', () => { // Regression for O(n^2) backtracking on unclosed ` { const liquid = new Liquid() const payload = ' { const liquid = new Liquid() const payload = ' but no in linear time', () => { const liquid = new Liquid() const payload = '