fix(strip_html): avoid infinite loop on unclosed openers

Change the stall guard from i === lt to i <= lt (GHSA-m7fp-h3p4-hr49).
Document that strip_html output is not safe for HTML without escape.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Yang Jun
2026-06-22 01:43:34 +08:00
co-authored by Cursor
parent 03a30e6dc4
commit f64f04c562
4 changed files with 14 additions and 1 deletions
+1
View File
@@ -11,6 +11,7 @@ coverage/
node_modules/
# tmp
.local/
docs/themes/navy/source/js/liquid.browser.min.js
docs/themes/navy/layout/partial/all-contributors.swig
docs/themes/navy/layout/partial/financial-contributors.swig
+8
View File
@@ -6,6 +6,10 @@ title: strip_html
Removes any HTML tags from a string.
{% note warn Not safe for HTML output %}
This filter is **not** a sanitizer. Output may still contain `<` sequences (for example malformed tags without a closing `>`, same as [Shopify Liquid](https://shopify.dev/docs/api/liquid/filters/strip_html)). Do not write the result into HTML without also using [escape][escape], [escape_once][escape_once], or [`outputEscape: "escape"`][outputEscape].
{% endnote %}
Input
```liquid
{{ "Have <em>you</em> read <strong>Ulysses</strong>?" | strip_html }}
@@ -15,3 +19,7 @@ Output
```text
Have you read Ulysses?
```
[escape]: ./escape.html
[escape_once]: ./escape.html
[outputEscape]: ../tutorials/options.html#outputEscape
+1 -1
View File
@@ -60,7 +60,7 @@ export function strip_html (this: FilterImpl, v: string) {
if (e >= 0) { i = e + closer.length; break }
blocks.delete(opener)
}
if (i === lt) return out + str.slice(lt)
if (i <= lt) return out + str.slice(lt)
}
return out
}
+4
View File
@@ -85,5 +85,9 @@ describe('filters/html', function () {
expect(liquid.parseAndRenderSync('{{"<img\rsrc=x\ronerror=alert(1)>" | strip_html}}')).toBe('')
expect(liquid.parseAndRenderSync('{{"<svg\nonload=alert(1)>" | strip_html}}')).toBe('')
})
it('should not loop on unclosed openers (GHSA-m7fp-h3p4-hr49)', function () {
expect(liquid.parseAndRenderSync('{{ "a<" | strip_html }}')).toBe('a<')
expect(liquid.parseAndRenderSync('{{ "hello<world<again" | strip_html }}')).toBe('hello<world<again')
})
})
})