LiquidJS Vulnerable to ReDoS via Quadratic Backtracking in strip_html Filter Regex
Summary
The built-in strip_html filter in liquidjs uses a regex containing four lazy-quantified alternatives. When the input contains many ||<.*?>|/g, '')
}
The regex contains four lazy patterns:
<.*?>
<!--[\s\S]*?-->
For an input like ' {
for (const n of [1000, 2000, 4000, 8000, 16000]) {
const payload = ' {
const payload = ')[^<]*)*<\/script>|)[^<]*)*<\/style>|<!--[^-]*(?:-(?!->)[^-]*)*-->|<[^>]*>/g,
''
)
This unrolls each lazy quantifier so each < is visited at most a constant number of times overall — linear total work.
Option 2 — single-pass tokenizer in plain code; iterate over the string once, tracking whether you are inside `, , comment, or generic tag, and emit nothing for those ranges.
Either fix should be combined with charging the regex output cost honestly to memoryLimit` and (defensively) capping input length up front:
export function strip_html (this: FilterImpl, v: string) {
const str = stringify(v)
this.context.memoryLimit.use(str.length)
// ... linear-time strip implementation here
}