Cloudanix VulnDB

CVE-2026-44645

Medium · 6.5

CVSS

6.5

EPSS

0.6%

Published

Jun 17, 2026

CISA due date

—

Cloudanix analysis

Next patch cycle SSVC: Track

A user who can create templates may be able to make the service slow down or stop responding by submitting a specially crafted template.

Exploitation
Remote Authenticated
Attacker needs
Low Privilege
Complexity
Low
Impact
Denial Of Service
Component
Library
Class
Denial of service
Remediation
Upgrade
Effort
Medium
Patch risk
Low

What it actually does

LiquidJS versions 10.25.7 and earlier contain a denial-of-service weakness in the renderLimit enforcement path. The renderLimit control is intended to cap the time spent in a render() or parseAndRenderSync() call, but LiquidJS only performs the per-iteration time check for {% for %} and {% tablerow %} loops when the loop body contains at least one template node. An attacker who can submit or edit templates can create an empty-body loop over a large range, causing LiquidJS to iterate for an attacker-chosen duration without consulting renderLimit. This can monopolize a Node.js event-loop worker, delaying or stalling other in-flight requests. The impact is availability-only; the vulnerability does not directly allow data disclosure, data modification, or code execution.

Why it matters to the business

Systems that allow users, tenants, or integrations to author LiquidJS templates may be vulnerable to low-cost service disruption even when renderLimit is configured as a denial-of-service safeguard. A single crafted template can consume a Node.js worker for far longer than the configured limit, causing request latency spikes, timeouts, worker exhaustion, degraded customer-facing functionality, or partial outages. The risk is most relevant to multi-tenant template-authoring, site-generation, email/template preview, CMS, and automation platforms that rely on LiquidJS renderLimit for isolation. Although this issue is not listed in CISA KEV, has no known ransomware association, and currently has low EPSS, the attack is simple for any low-privileged template author because it requires only template submission capability.

How an attack unfolds

An attacker first obtains access to a feature that renders user-controlled LiquidJS templates, such as a theme editor, page builder, email template editor, automation rule, or document-generation workflow. They then submit a Liquid template containing an empty {% for %} or {% tablerow %} body over a very large collection or numeric range, for example an empty loop over 1..N. Because the loop body contains no template node, vulnerable LiquidJS versions do not perform the renderLimit time check during each iteration. When the application renders the template, the Node.js event loop spends CPU time walking the full range until completion or another resource limit is reached. Repeated submissions or concurrent rendering can tie up multiple workers, producing application timeouts and degraded availability.

How to fix it

Upgrade LiquidJS to version 10.26.0 or later everywhere it is used. If LiquidJS is present transitively through another package, upgrade the dependent package or override/pin the LiquidJS dependency to a fixed version where compatible. Until the upgrade is deployed, reduce exposure by restricting template-authoring privileges, disabling or constraining untrusted Liquid template rendering, adding application-level request timeouts, limiting maximum numeric ranges and collection sizes available to templates, rejecting empty-body {% for %} and {% tablerow %} constructs from untrusted templates, and isolating rendering in worker processes that can be terminated when they exceed CPU or wall-clock limits. Do not rely on renderLimit alone as the only control on vulnerable versions.

How to confirm the fix worked

Confirm the deployed dependency tree no longer contains LiquidJS 10.25.7 or earlier by checking package lockfiles, SBOMs, container images, and runtime dependency listings. For npm-based projects, use commands such as npm ls liquidjs, yarn why liquidjs, or pnpm why liquidjs and verify the resolved version is 10.26.0 or later. Exercise a safe performance regression test in a non-production environment using an empty-body loop with a large range and a low renderLimit; fixed versions should interrupt or bound rendering according to the configured limit rather than allowing long unbounded iteration. Also verify compensating controls such as template linting, maximum range restrictions, rendering worker timeouts, and monitoring alerts for render latency are active.

Typical attack path

  1. Identify an application feature that accepts or stores user-controlled LiquidJS templates.
  2. Confirm that submitted templates are rendered server-side by a Node.js service using LiquidJS.
  3. Submit a crafted empty-body {% for %} or {% tablerow %} loop over a large range or collection.
  4. Trigger rendering of the template through preview, publish, email generation, page generation, or an API call.
  5. The vulnerable renderer iterates through the full loop without enforcing renderLimit.
  6. Repeat or parallelize rendering attempts to increase event-loop blocking, latency, and request failures.

How to detect it

Look for evidence of abuse in template storage, template audit logs, web/API access logs, application render logs, APM traces, and Node.js runtime telemetry. Focus on unusual spikes in template render duration, event-loop lag, CPU saturation, request timeouts, worker restarts, or repeated preview/render requests by the same user, token, IP address, or integration. Search stored and recently submitted templates for empty-body Liquid loops using {% for %} or {% tablerow %}, especially loops over large numeric ranges such as (1..100000000). In HTTP logs, review POST/PUT/PATCH requests to template creation, template update, preview, render, theme, page, email, or document-generation endpoints that contain encoded Liquid syntax. In application logs, correlate long render timings with submitted template content or template identifiers. Because this vulnerability causes availability impact rather than code execution, indicators are primarily malicious template content and performance artifacts, not dropped files or persistent host-level changes.

  • {%- for i in (1..
  • {% for i in (1..
  • {%- endfor -%}
  • {% endfor %}
  • {% tablerow
  • {% endtablerow %}
  • %7B%25%20for%20
  • %7B%25-%20for%20
  • %7B%25%20tablerow%20
  • %7B%25%20endfor%20%25%7D
  • %7B%25-%20endfor%20-%25%7D
  • parseAndRenderSync
  • renderLimit
  • LiquidJS

What your security team should do

  • Inventory all applications, services, build pipelines, and container images that use LiquidJS directly or transitively.
  • Prioritize upgrades for internet-facing or multi-tenant systems that allow low-privileged users to create, edit, preview, or publish Liquid templates.
  • Upgrade LiquidJS to 10.26.0 or later and redeploy affected services.
  • Add a temporary template validation rule that blocks empty-body {% for %} and {% tablerow %} loops from untrusted authors until all vulnerable versions are removed.
  • Place server-side template rendering behind strict wall-clock, CPU, concurrency, and queue limits, preferably in killable worker processes rather than the primary request event loop.
  • Review application logs and performance telemetry for unexplained render latency spikes, request timeouts, or repeated template preview/generation calls around the publication date and afterward.
  • Create SOC detections for suspicious Liquid templates containing large numeric ranges with empty loop bodies.
  • Update secure coding guidance to treat template rendering as untrusted code execution from an availability perspective, even when the template language is sandboxed.

The record

LiquidJS is a Shopify/GitHub Pages compatible template engine written in pure JavaScript. In versions 10.25.7 and below, the renderLimit option can be fully bypassed by a {% for %} (or {% tablerow %}) tag whose body is empty. The renderLimit option is documented in docs/source/tutorials/dos.md as the mechanism that "mitigates this by limiting the time consumed by each render() call." The per-iteration time check is reached only when the body contains at least one template node, so a template such as {%- 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. Deployments that rely on a finite renderLimit for DoS protection (common in multi-tenant template-authoring environments) can still be forced by a single crafted template to monopolize a Node.js event-loop worker for attacker-controlled time, potentially stalling in-flight requests, with availability impact only. This issue has been fixed in version 10.26.0.

Affected packages

Ecosystem Package Introduced Fixed
npm liquidjs 0 No fix

Related vulnerabilities