Skip to content

← Public intelligence

npm package

liquidjs

Open actionable advisories and recorded vendor fixes. This public package view has no inventory attached and cannot establish whether your deployment is affected. A vendor fix never resolves your finding; verify your installed version and the source guidance.

Open advisories

18 actionable advisories, newest first. Withdrawn advisories and news are excluded. Coverage is partial.

  1. Medium, CVSS 6.0npmAmendedPublished

    LiquidJS: ownPropertyOnly bypass for inherited array indices in first/last/join/reverse/slice/compact, .first/.last, negative index, and for-loop iteration

    CVE-2026-106120 · GHSA-fwxr-j5w2-587m

    Affected
    • npm liquidjs <= 10.27.1
    Fixed in
    10.27.2· Vendor fix available
    Issue
    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: correctly blocks an inherited array index. However, the same inherited value is still disclosed through: Impact This is an information disclosure issue when an application relies on ownPropertyOnly: true to safely render templates over untrusted or prototype-polluted scope data. Object prototype property reads are blocked as expected, but inherited Array.prototype index reads are not consistently blocked. An attacker who can influence prototype state or inherited array-index data may cause templates to disclose values that ownPropertyOnly is expected to hide. Proof of concept Observed behavior Expected behavior When ownPropertyOnly: true is enabled, inherited array indices should be treated as absent, matching the behavior of: Inherited or prototype-provided array-index values should not be…

    Recommended action: Upgrade to 10.27.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:github.com (1)osv.dev (2)Source checked Evidence →
  2. High, CVSS 7.5npmAmendedPublished

    LiquidJS: Uncontrolled Resource Consumption in join filter allows template authors to bypass memoryLimit and crash the process

    CVE-2026-69222 · GHSA-4r6h-5v86-94p3

    Affected
    • npm liquidjs <= 10.27.1
    Fixed in
    10.27.2· Vendor fix available
    Issue
    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 concat (array.ts:72) is the enabler: it charges by element count too, but only copies references (cheap for both limiter and heap), so an array's element count can be doubled repeatedly at near-zero real cost. join is where the bug lives — it's the call that actually materializes all referenced content into one string, and its own charge (array.length) doesn't reflect that. Same undercounting class as already-fixed replace (GHSA-mmg9-6m6j-jqqx), replacefirst (GHSA-6q5m-63h6-5x4v), date/strftime (GHSA-hh27-hf48-9f5q) — join wasn't covered. Sibling arraytosentencestring (src/filters/string.ts:210) has the identical defect. PoC Live-reproduced against liquidjs@10.27.1, Node v24.3.0. Verified by…

    Recommended action: Upgrade to 10.27.2 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:github.com (1)osv.dev (2)Source checked Evidence →
  3. High, CVSS 8.7npmPublished

    LiquidJS has an infinite loop vulnerability in its strip_html filter

    CVE-2026-61556 · GHSA-m7fp-h3p4-hr49

    Affected
    • npm liquidjs >= 10.26.0, < 10.27.1
    Fixed in
    10.27.1· Vendor fix available
    Issue
    The current implementation of striphtml 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. For the input "a<", the variable lt is updated to 1 by const lt = str.indexOf('<', i). However, the variable i is never updated from its initial value of 0. This is because in const e = str.indexOf(closer, lt + opener.length), e becomes -1, since there is no after <. Therefore, when execution reaches if (i === lt) return out + str.slice(lt), i is 0. This is the same state as at the beginning of the loop. As a result, the same thing is repeated again from that state, causing an infinite loop. PoC Impact This is an infinite loop vulnerability (cf. https://cwe.mitre.org/data/definitions/835.html). This results in a denial of service (DoS). Although a ReDoS vulnerability has previously been reported in the affected…

    Recommended action: Upgrade to 10.27.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:github.comSource checked Evidence →
  4. High, CVSS 8.2npmAmendedPublished

    LiquidJS: pop filter bypasses memoryLimit accounting that its array-filter siblings enforce

    CVE-2026-55575 · GHSA-g357-x5c3-c72p

    Affected
    • npm liquidjs <= 10.27.0
    Fixed in
    10.27.1· Vendor fix available
    Issue
    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, map, sortBy, where, groupby, uniq). This silently disables the memoryLimit budget for {{ hugearray | pop }}, letting a template render allocate an O(N) clone of an attacker-influenced array regardless of how strictly memoryLimit is set. Affected - liquidjs ≥ all versions that ship the current pop filter implementation (verified 10.27.0, HEAD a8fd734b5) - Deployments where any template uses {{ arr | pop }} on an array whose length is…

    Recommended action: Upgrade to 10.27.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  5. Critical, CVSS 10.0npmAmendedPublished

    LiquidJS is Vulnerable to Remote Code Execution

    CVE-2026-45618 · GHSA-gf2q-c269-pqgc

    Affected
    • npm liquidjs < 10.26.0
    Fixed in
    10.26.0· Vendor fix available
    Issue
    It is possible to execute arbitrary code with crafted templates Details <details <summary 1|valueOf - this when evaluating the filter </summary </details <details <summary function calls with a controlled first argument via comprable </summary <img width="1426" height="717" alt="image" src="https://github.com/user-attachments/assets/0618eb81-fb0d-4100-a6a0-556982decf8a" / </details <details<summarychanging the prototype of things</summary <img width="723" height="211" alt="image" src="https://github.com/user-attachments/assets/c05f4c4a-4151-4765-b569-3300ad837668" / </details When calling functions via the comparable gadget, this will be the scope. By overwriting this.loader.lookup and this.readFile, to fully control what goes into this.parse, and while controlling this, a reference to the Function constructor can be obtained, which then allows executing arbitrary code. PoC Complete instructions, including specific configuration details, to reproduce the vulnerability. Impact…

    Recommended action: Upgrade to 10.26.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:github.com (1)osv.dev (2)Source checked Evidence →
  6. High, CVSS 7.5npmAmendedPublished

    LiquidJS Vulnerable to ReDoS via Quadratic Backtracking in strip_html Filter Regex

    CVE-2026-45617 · GHSA-r7g9-xpmj-5fcq

    Affected
    • npm liquidjs < 10.26.0
    Fixed in
    10.26.0· Vendor fix available
    Issue
    The built-in striphtml 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 striphtml only charges str.length to the limit — the regex itself runs unbounded. Details The vulnerable filter is at src/filters/html.ts:45-49: The regex contains four lazy patterns: 1. <script[\s\S]?<\/script 2. <style[\s\S]?<\/style 3. <.? 4. <!--[\s\S]?-- For an input like '<script'.repeat(N), the engine encounters N starting < positions. At each one it must lazily expand [\s\S]? (and .?) all the way to end-of-input searching for a closer that never appears, then fail and backtrack. Because…

    Recommended action: Upgrade to 10.26.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  7. High, CVSS 7.5npmAmendedPublished

    LiquidJS has a memory and render limit bypass via unbounded width padding in date filter (strftime)

    CVE-2026-45357 · GHSA-hh27-hf48-9f5q

    Affected
    • npm liquidjs < 10.26.0
    Fixed in
    10.26.0· Vendor fix available
    Issue
    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 entirely bypassed. Details `date.ts:5-13` only charges `memoryLimit` for the lengths of the input value, format string, and timezone: ```ts export function date (this: FilterImpl, v: string | Date, format?: string, timezoneOffset?: number | string) { const size = ((v as string)?.length ?? 0) + (format?.length ?? 0) + ((timezoneOffset as string)?.length ?? 0) this.context.memoryLimit.use(size) ...…

    Recommended action: Upgrade to 10.26.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  8. Medium, CVSS 5.3npmAmendedPublished

    LiquidJS's {% render %} tag silently bypasses per-render ownPropertyOnly:true via Context.spawn()

    CVE-2026-44646 · GHSA-9x9p-qf8f-mvjg

    Affected
    • npm liquidjs < 10.26.0
    Fixed in
    10.26.0· Vendor fix available
    Issue
    `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 prototype-chain properties from inside any `{% render %}` partial. This is a distinct exploit surface from the previously identified array-filter variants (`where`, `reject`, `group_by`, `find`, `find_index`, `has`) — the underlying root cause in `Context.spawn()` is shared, but `{% render %}` is a separately reachable sink that needs no filter usage. Details The bug is in `Context.spawn()`: ```ts //…

    Recommended action: Upgrade to 10.26.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  9. Medium, CVSS 6.5npmAmendedPublished

    LiquidJS has a renderLimit DoS guard bypass via empty {% for %} body

    CVE-2026-44645 · GHSA-8xx9-69p8-7jp3

    Affected
    • npm liquidjs < 10.26.0
    Fixed in
    10.26.0· Vendor fix available
    Issue
    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 seconds (~45× over the limit) and scales linearly with `N` up to `memoryLimit`, allowing a low-privileged template author to wedge an event-loop thread for an attacker-chosen duration. Details `Render.renderTemplates` is the single point at which `renderLimit` is consulted: ```ts // src/render/render.ts 14: public * renderTemplates (templates: Template[], ctx: Context, emitter?: Emitter):…

    Recommended action: Upgrade to 10.26.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  10. Medium, CVSS 6.1npmAmendedPublished

    LiquidJS's strip_html filter bypass via newline characters in HTML tags enables XSS

    CVE-2026-44644 · GHSA-2qv6-9wx5-cwv4

    Affected
    • npm liquidjs < 10.26.0
    Fixed in
    10.26.0· Vendor fix available
    Issue
    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. ` `) 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 reflected XSS in any application that relies on `strip_html` to neutralize untrusted HTML. Details The vulnerable code is in `src/filters/html.ts`: ```ts // src/filters/html.ts:45-49 export function strip_html (this: FilterImpl, v: string) { const str = stringify(v) this.context.memoryLimit.use(str.length) return str.replace(/ | | | /g, '') } ``` The regex has four alternations: 1. ` ` — uses `[\s\S]`, matches across newlines. 2. ` `…

    Recommended action: Upgrade to 10.26.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  11. High, CVSS 7.5npmAmendedPublished

    liquidjs has a Denial of Service via circular block reference in layout

    CVE-2026-41311 · GHSA-4rc3-7j7w-m548

    Affected
    • npm liquidjs < 10.25.7
    Fixed in
    10.25.7· Vendor fix available
    Issue
    A circular block reference in {% layout %} / {% block %} causes an infinite recursive loop, consuming all available memory (~4GB) and crashing the Node.js process with FATAL ERROR: JavaScript heap out of memory. This allows any user who can submit a Liquid template to perform a Denial of Service attack. Details In src/tags/block.ts, during OUTPUT mode, each block looks up its render function from ctx.getRegister('blocks')[this.block]. When a block with name a is nested inside another block also named a in a child template, the inner block finds the outer block's render function and calls it. The outer block's templates contain the inner block again, creating infinite recursion with no termination condition. Relevant code (src/tags/block.ts, getBlockRender method): When renderChild exists (same-name block found), it calls renderChild which re-renders templates containing the nested block, which again finds renderChild, and so on — infinite loop. PoC 1. Create a layout file…

    Recommended action: Upgrade to 10.25.7 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  12. Medium, CVSS 6.3npmAmendedPublished

    LiquidJS: renderFile() / parseFile() bypass configured root and allow arbitrary file read

    CVE-2026-39859 · GHSA-v273-448j-v4qj

    Affected
    • npm liquidjs <= 10.25.4
    Fixed in
    10.25.5· Vendor fix available
    Issue
    liquidjs 10.25.0 documents root as constraining filenames passed to renderFile() and parseFile(), but top-level file loads do not enforce that boundary. The published npm package liquidjs@10.25.0 on Linux 6.17.0 with Node v22.22.1. A Liquid instance configured with an empty temporary directory as root still returned the contents of /etc/hosts when renderFile('/etc/hosts') was called. I have not exhaustively checked older releases yet; 10.25.0 is the latest tested version. Root cause: - src/parser/parser.ts:83-85 calls loader.lookup(file, LookupType.Root, ...) and then reads the returned file. - src/fs/loader.ts:38 passes type !== LookupType.Root into candidates(). - For LookupType.Root, enforceRoot is false, so src/fs/loader.ts:47-66 accepts resolved absolute paths and fallback results without any contains() check. This appears adjacent to the March 10, 2026 fix for CVE-2026-30952, which hardened include / render / layout but not the top-level file-loading APIs. Proof of concept:…

    Recommended action: Upgrade to 10.25.5 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  13. Medium, CVSS 5.3npmAmendedPublished

    LiquidJS: ownPropertyOnly bypass via sort_natural filter — prototype property information disclosure through sorting side-channel

    CVE-2026-39412 · GHSA-rv5g-f82m-qrvv

    Affected
    • npm liquidjs <= 10.25.3
    Fixed in
    10.25.4· Vendor fix available
    Issue
    The sortnatural filter bypasses the ownPropertyOnly security option, allowing template authors to extract values of prototype-inherited properties through a sorting side-channel attack. Applications relying on ownPropertyOnly: true as a security boundary (e.g., multi-tenant template systems) are exposed to information disclosure of sensitive prototype properties such as API keys and tokens. Details In src/filters/array.ts, the sortnatural function (lines 40-48) accesses object properties using direct bracket notation (lhs[propertyString]), which traverses the JavaScript prototype chain: In contrast, the correct approach used elsewhere in the codebase goes through readJSProperty in src/context/context.ts, which checks hasOwnProperty when ownPropertyOnly is enabled: The sortnatural filter bypasses this check entirely. The sort filter (lines 26-38 in the same file) has the same issue. PoC Result: The sorted order reveals that the target's prototype apiKey falls between "aaa" and…

    Recommended action: Upgrade to 10.25.4 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  14. High, CVSS 8.2npmAmendedPublished

    LiquidJS: Root restriction bypass for partial and layout loading through symlinked templates

    CVE-2026-35525 · GHSA-56p5-8mhr-2fph

    Affected
    • npm liquidjs <= 10.25.2
    Fixed in
    10.25.3· Vendor fix available
    Issue
    LiquidJS enforces partial and layout root restrictions using the resolved pathname string, but it does not resolve the canonical filesystem path before opening the file. A symlink placed inside an allowed partials or layouts directory can therefore point to a file outside that directory and still be loaded. Details For {% include %}, {% render %}, and {% layout %}, LiquidJS checks whether the candidate path is inside the configured partials or layouts roots before reading it. That check is path-based, not realpath-based. Because of that, a file like partials/link.liquid passes the directory containment check as long as its pathname is under the allowed root. If link.liquid is actually a symlink to a file outside the allowed root, the filesystem follows the symlink when the file is opened and LiquidJS renders the external target. So the restriction is applied to the path string that was requested, not to the file that is actually read. This matters in environments where an…

    Recommended action: Upgrade to 10.25.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  15. Low, CVSS 3.7npmAmendedPublished

    LiquidJS Has Memory Limit Bypass via Quadratic Amplification in replace Filter

    CVE-2026-34166 · GHSA-mmg9-6m6j-jqqx

    Affected
    • npm liquidjs <= 10.25.2
    Fixed in
    10.25.3· Vendor fix available
    Issue
    The replace filter in LiquidJS incorrectly accounts for memory usage when the memoryLimit option is enabled. It charges str.length + pattern.length + replacement.length bytes to the memory limiter, but the actual output from str.split(pattern).join(replacement) can be quadratically larger when the pattern occurs many times in the input string. This allows an attacker who controls template content to bypass the memoryLimit DoS protection with approximately 2,500x amplification, potentially causing out-of-memory conditions. Details The vulnerable code is in src/filters/string.ts:137-142: The memoryLimit.use() call charges only the sum of the three input lengths. However, the str.split(pattern).join(replacement) operation produces output of size: When every character in str matches pattern (e.g., str = 5,000 as, pattern = a), there are 5,000 occurrences. With a 5,000-character replacement string, the output is 5000 5000 = 25,000,000 characters, while only 5000 + 1 + 5000 =…

    Recommended action: Upgrade to 10.25.3 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  16. High, CVSS 7.5npmAmendedPublished

    LiquidJS has Exponential Memory Amplification through its replace_first Filter $& Pattern

    CVE-2026-33287 · GHSA-6q5m-63h6-5x4v

    Affected
    • npm liquidjs < 10.25.1
    Fixed in
    10.25.1· Vendor fix available
    Issue
    The `replace_first` filter in LiquidJS uses JavaScript's `String.prototype.replace()` which interprets `$&` as a backreference to the matched substring. The filter only charges `memoryLimit` for the input string length, not the amplified output. An attacker can achieve exponential memory amplification (up to 625,000:1) while staying within the `memoryLimit` budget, leading to denial of service. Details The `replace_first` filter in `src/builtin/filters/string.ts:130-133` delegates to JavaScript's native `String.prototype.replace()`. This native method interprets special replacement patterns including `$&` (insert the matched substring), `$'` (insert the portion after the match), and `` $` `` (insert the portion before the match). The filter calls `memoryLimit.use(str.length)` to account for the input string's memory cost, but the output string — potentially many times larger due to `$&` expansion — is never charged against the memory limit. An attacker can…

    Recommended action: Upgrade to 10.25.1 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  17. High, CVSS 8.7npmAmendedPublished

    liquidjs has a path traversal fallback vulnerability

    CVE-2026-30952 · GHSA-wmfp-5q7x-987x

    Affected
    • npm liquidjs < 10.25.0
    Fixed in
    10.25.0· Vendor fix available
    Issue
    Impact The layout, render, and include tags allow arbitrary file access via absolute paths (either as string literals or through Liquid variables, the latter require dynamicPartials: true, which is the default). This poses a security risk when malicious users are allowed to control the template content or specify the filepath to be included as a Liquid variable. Patches The root cause is LiquidJS allows require.resolve() as fallback but doesn't limit the directories it can resolve to. The issue is fixed via #855 and published version 10.25.0 on npm. Workarounds Change the files in build time In build time, through Shell script or Webpack string-replace-loader, change the file content of correxponding file (depending on your package type, for CommonJS it's dist/liquid.node.js) under dist/, Overriding by fs LiquidJS option Adding a fs option to override the default fs implementation: References Discussions: https://github.com/harttle/liquidjs/pull/851 Code fix:…

    Recommended action: Upgrade to 10.25.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →
  18. Medium, CVSS 5.3npmAmendedPublished

    liquidjs may leak properties of a prototype

    CVE-2022-25948 · GHSA-45rm-2893-5f49

    Affected
    • npm liquidjs < 10.0.0
    Fixed in
    10.0.0· Vendor fix available
    Issue
    The package liquidjs before 10.0.0 is vulnerable to Information Exposure when ownPropertyOnly parameter is set to False, which results in leaking properties of a prototype. Workaround For versions 9.34.0 and higher, an option to disable this functionality is provided.

    Recommended action: Upgrade to 10.0.0 or later, then redeploy and verify.A vendor fix does not resolve your finding until you verify it in your own systems.

    Sources:osv.dev (1)github.com (2)Source checked Evidence →