liquidjs
Ecosystem: npm
Downloads: 11 Mln
- 1CriticalAffects 1 version
- 10HighAffects 10 versions
- 0MediumAffects 0 versions
- 7ModerateAffects 7 versions
- 1LowAffects 1 version
- Critical1 (5.3%)
- High10 (52.6%)
- Medium0 (0.0%)
- Moderate7 (36.8%)
- Low1 (5.3%)
Monthly breakdown
- Critical
- High
- Medium
- Moderate
- Low
- 1 vulnerabilities
- Critical: 0
- High: 0
- Medium: 0
- Moderate: 1
- Low: 0
- 2 vulnerabilities
- Critical: 0
- High: 2
- Medium: 0
- Moderate: 0
- Low: 0
- 1 vulnerabilities
- Critical: 0
- High: 1
- Medium: 0
- Moderate: 0
- Low: 0
- 6 vulnerabilities
- Critical: 1
- High: 2
- Medium: 0
- Moderate: 3
- Low: 0
- 5 vulnerabilities
- Critical: 0
- High: 2
- Medium: 0
- Moderate: 2
- Low: 1
- 3 vulnerabilities
- Critical: 0
- High: 3
- Medium: 0
- Moderate: 0
- Low: 0
- 1 vulnerabilities
- Critical: 0
- High: 0
- Medium: 0
- Moderate: 1
- Low: 0
Vulnerabilities (19)
Known vulnerabilities affecting liquidjs, based on data from OSV.
| ID | Severity | Summary | Affected versions | Published | Source |
|---|---|---|---|---|---|
| GHSA-fwxr-j5w2-587m | Moderate | LiquidJS: ownPropertyOnly bypass for inherited array indices in first/last/join/reverse/slice/compact, `.first`/`.last`, negative index, and for-loop iteration## Summary LiquidJS’s `ownPropertyOnly` option is intended to prevent templates from reading inherited or prototype properties from scope data. With `ownPropertyOnly: true`, direct access such as: ```liquid {{ a[0] }} ``` correctly blocks an inherited array index. However, the same inherited value is still disclosed through: ```text .first .last negative indexing for-loop iteration first last join reverse slice compact ``` ## Impact This is an information disclosure issue when an application relies on `ownPropertyOnly: true` to safely render templates over untrusted or prototype-pollute |
| GitHub (opens in a new tab) | |
| GHSA-4r6h-5v86-94p3 | High | LiquidJS: Uncontrolled Resource Consumption in `join` filter allows template authors to bypass `memoryLimit` and crash the process### Summary The `join` filter (`src/filters/array.ts:8-13`) charges `memoryLimit` by array element **count**, not by the string length it produces, letting a template bypass a configured `memoryLimit` and allocate strings far past budget — bounded only by V8/process limits, not by `memoryLimit`. ### Details ```js // src/filters/array.ts:8-13 export const join = argumentsToValue(function (this: FilterImpl, v: any[], arg: string) { const array = toArray(v) const sep = isNil(arg) ? ' ' : stringify(arg) const complexity = array.length * (1 + sep.length) // element COUNT, not element siz |
| GitHub (opens in a new tab) | |
| GHSA-m7fp-h3p4-hr49 | High | LiquidJS has an infinite loop vulnerability in its `strip_html` filter### Summary The current implementation of `strip_html` can cause an infinite loop when the input string contains `<`, has at least one character before `<`, and no `>` appears after `<`. ### Details The problem is in `src/filters/html.ts`. Specifically, the following part has the infinite loop. ``` // Raw-text blocks (HTML5) plus '<...>' as the catch-all kind; a regex // equivalent is O(n^2) in V8 on unclosed openers. export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) const blocks = new Map([['<script', '</script |
| GitHub (opens in a new tab) | |
| GHSA-g357-x5c3-c72p | High | LiquidJS: `pop` filter bypasses `memoryLimit` accounting that its array-filter siblings enforce# `pop` filter bypasses `memoryLimit` accounting that its array-filter siblings enforce **CWE**: CWE-770 (Allocation of Resources Without Limits or Throttling) — sibling class of GHSA-8xx9-69p8-7jp3 and GHSA-2546-xv4c-mc8g, applied to `memoryLimit` instead of `renderLimit` ## Summary The `pop` array filter at `src/filters/array.ts:91-95` allocates a full clone of its input array via `[...toArray(v)]` but does **not** call `this.context.memoryLimit.use(...)` the way every other array-clone filter in the same file does (`shift`, `unshift`, `compact`, `concat`, `reverse`, `sample`, `slice`, `m |
| GitHub (opens in a new tab) | |
| GHSA-2qv6-9wx5-cwv4 | Moderate | LiquidJS's strip_html filter bypass via newline characters in HTML tags enables XSS## Summary The `strip_html` filter in liquidjs is intended to remove HTML tags from a string before rendering, and is widely used as an XSS sanitizer. The implementation uses a regex whose catch-all branch (`<.*?>`) does not match line terminators, so any HTML tag containing a `\n` or `\r` character passes through unmodified. An attacker who can place a newline inside a tag (e.g. `<img\nsrc=x\nonerror=alert(1)>`) bypasses sanitization entirely, since browsers treat newlines as whitespace within a tag and execute the resulting `onerror`/`onload`/etc. handler. This results in stored or reflecte |
| GitHub (opens in a new tab) | |
| GHSA-8xx9-69p8-7jp3 | Moderate | LiquidJS has a renderLimit DoS guard bypass via empty `{% for %}` body## Summary The `renderLimit` option — documented in `docs/source/tutorials/dos.md` as the mechanism that "mitigates this by limiting the time consumed by each render() call" — can be fully bypassed by a `{% for %}` (or `{% tablerow %}`) tag whose body is empty. The per-iteration time check is reached only when the body contains at least one template node, so a template like `{%- for i in (1..N) -%}{%- endfor -%}` iterates the full collection without ever consulting `renderLimit`. With a configured `renderLimit` of 50 ms, a single `parseAndRenderSync` call has been observed to consume **2.26 s |
| GitHub (opens in a new tab) | |
| GHSA-9x9p-qf8f-mvjg | Moderate | LiquidJS's `{% render %}` tag silently bypasses per-render `ownPropertyOnly:true` via `Context.spawn()`## Summary `Context.spawn()` in liquidjs creates a child `Context` for the `{% render %}` tag but does not propagate the parent context's resolved `ownPropertyOnly` value. The new context re-derives `ownPropertyOnly` from `opts.ownPropertyOnly` (the instance-level option), silently discarding any `RenderOptions.ownPropertyOnly` override that was supplied to `parseAndRender()`. As a result, a developer who runs a Liquid instance with the backwards-compatible `ownPropertyOnly:false` and then locks down an untrusted render with `parseAndRender(..., { ownPropertyOnly: true })` still leaks prototy |
| GitHub (opens in a new tab) | |
| GHSA-gf2q-c269-pqgc | Critical | LiquidJS is Vulnerable to Remote Code Execution### Summary It is possible to execute arbitrary code with crafted templates ### Details <details> <summary> `1|valueOf` -> `this` when evaluating the filter </summary> ```liquid {%assign r=1|valueOf%} {{r|inspect}} ``` ```json {"context":{"scopes":[{"r":"[Circular]"}],"registers":{},"breakCalled":false,"continueCalled":false,"sync":false,"opts":{"root":["."],"layouts":["."],"partials":["."],"relativeReference":true,"jekyllInclude":false,"keyValueSeparator":":","extname":"","fs":{"sep":"/"},"dynamicPartials":true,"jsTruthy":false,"dateFormat":"%A, %B %-e, %Y at %-l:%M %P %z","locale":"e |
| GitHub (opens in a new tab) | |
| GHSA-hh27-hf48-9f5q | High | LiquidJS has a memory and render limit bypass via unbounded width padding in `date` filter (strftime)## Summary The `date` filter's strftime implementation parses width specifiers like `%9999999d` and forwards the captured width unchecked into `pad()`/`padStart()` in `src/util/underscore.ts`. The pad loop performs unbounded string concatenation without consulting the Context's `memoryLimit` or `renderLimit`, so a single small template (`{{ x | date: '%5000000d' }}`) produces megabytes of output and unbounded CPU. The `memoryLimit` and `renderLimit` options the docs (`src/liquid-options.ts:87-92`) advertise as DoS controls — and which the docstring explicitly mentions for `strftime` — are ent |
| GitHub (opens in a new tab) | |
| GHSA-r7g9-xpmj-5fcq | High | 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 `<script`, `<style`, or `<!--` opener tokens without matching closers, the V8 regex engine performs O(N²) backtracking, blocking the Node.js event loop. A single ~350 KB request (`'<script'.repeat(50000)`) stalls the process for ~10 seconds; cost grows quadratically with input size. The default `memoryLimit: Infinity` does not bound regex CPU, and even when configured `strip_html` only charges `str.length` to the limit — the regex itself runs unbounde |
| GitHub (opens in a new tab) |
- GHSA-fwxr-j5w2-587mModerateLiquidJS: ownPropertyOnly bypass for inherited array indices in first/last/join/reverse/slice/compact, `.first`/`.last`, negative index, and for-loop iteration
< 10.27.2
## Summary LiquidJS’s `ownPropertyOnly` option is intended to prevent templates from reading inherited or prototype properties from scope data. With `ownPropertyOnly: true`, direct access such as: ```liquid {{ a[0] }} ``` correctly blocks an inherited array index. However, the same inherited value is still disclosed through: ```text .first .last negative indexing for-loop iteration first last join reverse slice compact ``` ## Impact This is an information disclosure issue when an application relies on `ownPropertyOnly: true` to safely render templates over untrusted or prototype-pollute
CVE: CVE-2026-106120
- GHSA-4r6h-5v86-94p3HighLiquidJS: Uncontrolled Resource Consumption in `join` filter allows template authors to bypass `memoryLimit` and crash the process
< 10.27.2
### Summary The `join` filter (`src/filters/array.ts:8-13`) charges `memoryLimit` by array element **count**, not by the string length it produces, letting a template bypass a configured `memoryLimit` and allocate strings far past budget — bounded only by V8/process limits, not by `memoryLimit`. ### Details ```js // src/filters/array.ts:8-13 export const join = argumentsToValue(function (this: FilterImpl, v: any[], arg: string) { const array = toArray(v) const sep = isNil(arg) ? ' ' : stringify(arg) const complexity = array.length * (1 + sep.length) // element COUNT, not element siz
CVE: CVE-2026-69222
- GHSA-m7fp-h3p4-hr49HighLiquidJS has an infinite loop vulnerability in its `strip_html` filter
>= 10.26.0, < 10.27.1
### Summary The current implementation of `strip_html` can cause an infinite loop when the input string contains `<`, has at least one character before `<`, and no `>` appears after `<`. ### Details The problem is in `src/filters/html.ts`. Specifically, the following part has the infinite loop. ``` // Raw-text blocks (HTML5) plus '<...>' as the catch-all kind; a regex // equivalent is O(n^2) in V8 on unclosed openers. export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) const blocks = new Map([['<script', '</script
CVE: CVE-2026-61556
- GHSA-g357-x5c3-c72pHighLiquidJS: `pop` filter bypasses `memoryLimit` accounting that its array-filter siblings enforce
< 10.27.1
# `pop` filter bypasses `memoryLimit` accounting that its array-filter siblings enforce **CWE**: CWE-770 (Allocation of Resources Without Limits or Throttling) — sibling class of GHSA-8xx9-69p8-7jp3 and GHSA-2546-xv4c-mc8g, applied to `memoryLimit` instead of `renderLimit` ## Summary The `pop` array filter at `src/filters/array.ts:91-95` allocates a full clone of its input array via `[...toArray(v)]` but does **not** call `this.context.memoryLimit.use(...)` the way every other array-clone filter in the same file does (`shift`, `unshift`, `compact`, `concat`, `reverse`, `sample`, `slice`, `m
CVE: CVE-2026-55575
- GHSA-2qv6-9wx5-cwv4ModerateLiquidJS's strip_html filter bypass via newline characters in HTML tags enables XSS
< 10.26.0
## Summary The `strip_html` filter in liquidjs is intended to remove HTML tags from a string before rendering, and is widely used as an XSS sanitizer. The implementation uses a regex whose catch-all branch (`<.*?>`) does not match line terminators, so any HTML tag containing a `\n` or `\r` character passes through unmodified. An attacker who can place a newline inside a tag (e.g. `<img\nsrc=x\nonerror=alert(1)>`) bypasses sanitization entirely, since browsers treat newlines as whitespace within a tag and execute the resulting `onerror`/`onload`/etc. handler. This results in stored or reflecte
CVE: CVE-2026-44644
- GHSA-8xx9-69p8-7jp3ModerateLiquidJS has a renderLimit DoS guard bypass via empty `{% for %}` body
< 10.26.0
## Summary The `renderLimit` option — documented in `docs/source/tutorials/dos.md` as the mechanism that "mitigates this by limiting the time consumed by each render() call" — can be fully bypassed by a `{% for %}` (or `{% tablerow %}`) tag whose body is empty. The per-iteration time check is reached only when the body contains at least one template node, so a template like `{%- for i in (1..N) -%}{%- endfor -%}` iterates the full collection without ever consulting `renderLimit`. With a configured `renderLimit` of 50 ms, a single `parseAndRenderSync` call has been observed to consume **2.26 s
CVE: CVE-2026-44645
- GHSA-9x9p-qf8f-mvjgModerateLiquidJS's `{% render %}` tag silently bypasses per-render `ownPropertyOnly:true` via `Context.spawn()`
< 10.26.0
## Summary `Context.spawn()` in liquidjs creates a child `Context` for the `{% render %}` tag but does not propagate the parent context's resolved `ownPropertyOnly` value. The new context re-derives `ownPropertyOnly` from `opts.ownPropertyOnly` (the instance-level option), silently discarding any `RenderOptions.ownPropertyOnly` override that was supplied to `parseAndRender()`. As a result, a developer who runs a Liquid instance with the backwards-compatible `ownPropertyOnly:false` and then locks down an untrusted render with `parseAndRender(..., { ownPropertyOnly: true })` still leaks prototy
CVE: CVE-2026-44646
- GHSA-gf2q-c269-pqgcCriticalLiquidJS is Vulnerable to Remote Code Execution
< 10.26.0
### Summary It is possible to execute arbitrary code with crafted templates ### Details <details> <summary> `1|valueOf` -> `this` when evaluating the filter </summary> ```liquid {%assign r=1|valueOf%} {{r|inspect}} ``` ```json {"context":{"scopes":[{"r":"[Circular]"}],"registers":{},"breakCalled":false,"continueCalled":false,"sync":false,"opts":{"root":["."],"layouts":["."],"partials":["."],"relativeReference":true,"jekyllInclude":false,"keyValueSeparator":":","extname":"","fs":{"sep":"/"},"dynamicPartials":true,"jsTruthy":false,"dateFormat":"%A, %B %-e, %Y at %-l:%M %P %z","locale":"e
CVE: CVE-2026-45618
- GHSA-hh27-hf48-9f5qHighLiquidJS has a memory and render limit bypass via unbounded width padding in `date` filter (strftime)
< 10.26.0
## Summary The `date` filter's strftime implementation parses width specifiers like `%9999999d` and forwards the captured width unchecked into `pad()`/`padStart()` in `src/util/underscore.ts`. The pad loop performs unbounded string concatenation without consulting the Context's `memoryLimit` or `renderLimit`, so a single small template (`{{ x | date: '%5000000d' }}`) produces megabytes of output and unbounded CPU. The `memoryLimit` and `renderLimit` options the docs (`src/liquid-options.ts:87-92`) advertise as DoS controls — and which the docstring explicitly mentions for `strftime` — are ent
CVE: CVE-2026-45357
- GHSA-r7g9-xpmj-5fcqHighLiquidJS Vulnerable to ReDoS via Quadratic Backtracking in `strip_html` Filter Regex
< 10.26.0
## Summary The built-in `strip_html` filter in liquidjs uses a regex containing four lazy-quantified alternatives. When the input contains many `<script`, `<style`, or `<!--` opener tokens without matching closers, the V8 regex engine performs O(N²) backtracking, blocking the Node.js event loop. A single ~350 KB request (`'<script'.repeat(50000)`) stalls the process for ~10 seconds; cost grows quadratically with input size. The default `memoryLimit: Infinity` does not bound regex CPU, and even when configured `strip_html` only charges `str.length` to the limit — the regex itself runs unbounde
CVE: CVE-2026-45617