Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-44645

CVE-2026-44645: LiquidJS renderLimit Bypass DoS Vulnerability

CVE-2026-44645 is a denial of service flaw in LiquidJS that allows attackers to bypass renderLimit protections using empty for loops. This article covers the technical details, affected versions, and mitigation strategies.

Published:

CVE-2026-44645 Overview

CVE-2026-44645 is a denial of service vulnerability in LiquidJS, a Shopify and GitHub Pages compatible template engine written in pure JavaScript. The flaw affects versions 10.25.7 and earlier. An attacker who can author templates can bypass the renderLimit safeguard by submitting a {% for %} or {% tablerow %} tag with an empty body. The per-iteration time check is never reached, allowing the loop to iterate the full collection unbounded. The issue is tracked under [CWE-400] Uncontrolled Resource Consumption and was fixed in version 10.26.0.

Critical Impact

A single crafted template can monopolize a Node.js event-loop worker for attacker-controlled duration, stalling in-flight requests in multi-tenant template-authoring environments.

Affected Products

  • LiquidJS versions 10.25.7 and below
  • Node.js applications embedding LiquidJS for multi-tenant template rendering
  • Deployments relying on renderLimit as the primary DoS mitigation

Discovery Timeline

  • 2026-06-17 - CVE-2026-44645 published to NVD
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2026-44645

Vulnerability Analysis

LiquidJS exposes a renderLimit option documented in docs/source/tutorials/dos.md as the mechanism that limits time consumed per render() call. The enforcement code performs its time check inside the per-template-node iteration loop. When the body of a {% for %} or {% tablerow %} block contains zero template nodes, the inner loop iterates the collection without ever invoking ctx.renderLimit.check(). A template such as {%- for i in (1..N) -%}{%- endfor -%} runs to completion regardless of elapsed time.

Observed behavior shows a parseAndRenderSync call consuming 2.26 seconds against a configured renderLimit of 50 ms, roughly 45 times over the limit. Execution time scales linearly with N up to memoryLimit, giving the template author direct control over how long the event-loop thread is blocked.

Root Cause

The root cause is a missing guard in the empty-body code path of the renderer. The renderLimit accounting was coupled to the rendering of child template nodes rather than to the iteration counter of the enclosing loop. Loops with no body therefore execute outside the protective window.

Attack Vector

The attack requires low-privileged authenticated access to submit or store a Liquid template. A low-privileged template author crafts a loop with a very large range and an empty body. When the host application renders the template, the synchronous render call pegs a single Node.js worker thread, stalling concurrent requests served by that process. Confidentiality and integrity are not affected; only availability is impacted.

typescript
// Patch in src/render/render.ts - enforce renderLimit for empty renderTemplates calls (#894)
     if (!emitter) {
       emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter()
     }
+    ctx.renderLimit.check(getPerformance().now())
     const errors = []
     for (const tpl of templates) {
       ctx.renderLimit.check(getPerformance().now())

Source: GitHub Commit 5b9c346. The fix adds an explicit renderLimit.check() call before the inner loop so that the budget is evaluated even when templates is empty.

Detection Methods for CVE-2026-44645

Indicators of Compromise

  • Sustained single-core CPU saturation in Node.js worker processes coinciding with template render requests.
  • HTTP request latency spikes or timeouts on endpoints that call parseAndRenderSync or parseAndRender.
  • Stored or submitted Liquid templates containing {% for %} or {% tablerow %} blocks with empty bodies and large iteration ranges such as (1..1000000).

Detection Strategies

  • Inventory all services using LiquidJS and confirm versions; flag any package-lock entries at or below 10.25.7.
  • Add static analysis or template linting that rejects loops with empty bodies or with iteration counts exceeding a safe threshold.
  • Instrument render calls with wall-clock measurements and alert when single renders exceed the configured renderLimit.

Monitoring Recommendations

  • Track event-loop lag metrics (e.g., perf_hooks monitorEventLoopDelay) on Node.js services that render user-supplied templates.
  • Log template author identity, template size, and render duration for each render call to support forensic correlation.
  • Alert on repeated long-running render operations originating from a single low-privileged tenant or account.

How to Mitigate CVE-2026-44645

Immediate Actions Required

  • Upgrade LiquidJS to version 10.26.0 or later across all affected services and rebuild dependent artifacts.
  • Audit existing stored templates for empty-body {% for %} and {% tablerow %} constructs and remove or quarantine offending entries.
  • Restrict template authoring privileges to trusted users until the upgrade is verified in production.

Patch Information

The fix is included in LiquidJS 10.26.0. The patch adds an explicit ctx.renderLimit.check(getPerformance().now()) call before the templates iteration loop in src/render/render.ts, ensuring the render budget is enforced even when the body produces no template nodes. See the GitHub Release v10.26.0 and the GitHub Security Advisory GHSA-8xx9-69p8-7jp3 for full details.

Workarounds

  • Reject or sanitize templates whose loop bodies are empty before passing them to LiquidJS.
  • Run template rendering inside a worker thread or child process with a hard wall-clock kill timer external to LiquidJS.
  • Reduce memoryLimit to cap the maximum iteration count an attacker can request.
bash
# Upgrade LiquidJS to the patched release
npm install liquidjs@^10.26.0
npm ls liquidjs

# Verify the installed version is 10.26.0 or later
node -e "console.log(require('liquidjs/package.json').version)"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.