Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Lifecycle Timeline
4Blast Radius
ecosystem impact- 22 npm packages depend on liquidjs (11 direct, 11 indirect)
Ecosystem-wide dependent count for version 10.25.7.
DescriptionGitHub Advisory
Summary
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):
private getBlockRender (ctx: Context) {
const { liquid, templates } = this
const renderChild = ctx.getRegister('blocks')[this.block]
const renderCurrent = function * (superBlock: BlockDrop, emitter: Emitter) {
ctx.push({ block: superBlock })
yield liquid.renderer.renderTemplates(templates, ctx, emitter)
ctx.pop()
}
return renderChild
? (superBlock: BlockDrop, emitter: Emitter) => renderChild(
new BlockDrop(
(emitter: Emitter) => renderCurrent(superBlock, emitter)
),
emitter)
: renderCurrent
}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 (layout.html):
<header>{% block a %}default-a{% endblock %}</header>
<main>{% block b %}default-b{% endblock %}</main>
<footer>{% block c %}default-c{% endblock %}</footer>2. Create a template that uses the layout:
{% layout "layout" %}
{% block a %}outer-a {% block a %}inner-a{% endblock %}{% endblock %}
{% block b %}content-b{% endblock %}
{% block c %}content-c{% endblock %}3. Render:
const { Liquid } = require('liquidjs')
const liquid = new Liquid({ root: './', extname: '.html' })
liquid.renderFile('template').then(console.log)
// Result: process hangs, memory grows to ~4GB, then crashes with OOMThe anonymous block variant also triggers the same issue:
{% layout "parent" %}
{%block%}A{%block%}B{%endblock%}{%endblock%}Impact
Denial of Service (DoS). Any application that accepts user-provided or user-influenced Liquid templates - such as CMS platforms, email template builders, multi-tenant SaaS products, or static site generators with untrusted input - can be crashed by a single malicious template. The attack requires no authentication beyond the ability to submit a template, and no special configuration. The Node.js process is killed by the OS due to memory exhaustion, causing complete service disruption.
AnalysisAI
Infinite recursion in LiquidJS template engine crashes Node.js processes via out-of-memory condition when attackers submit templates with circular block references. Unauthenticated remote attackers can consume ~4GB RAM and terminate any application accepting user-provided Liquid templates by nesting identically-named blocks within {% layout %} / {% block %} tags. Vendor patch available via GitHub commit e2311df. CVSS 7.5 (High) reflects network-accessible, low-complexity attack requiring no privileges or user interaction, causing complete availability loss.
Technical ContextAI
LiquidJS is a JavaScript implementation of the Liquid template language for Node.js applications, commonly used in CMS platforms, email builders, static site generators, and multi-tenant SaaS products. The vulnerability resides in src/tags/block.ts where the getBlockRender method processes template inheritance. During OUTPUT mode, blocks retrieve their render functions from ctx.getRegister('blocks')[this.block]. When a child template defines a block with name 'a' nested inside another block also named 'a', the inner block's lookup retrieves the outer block's render function, which re-renders templates containing the inner block, creating unbounded recursion. This matches CWE-674 (Uncontrolled Recursion) where the code lacks termination conditions for self-referential template structures. The npm package identifier is pkg:npm/liquidjs, affecting applications that parse untrusted or user-influenced Liquid markup without input validation on block nesting patterns.
RemediationAI
Immediately upgrade LiquidJS to the patched version containing commit e2311dfd6e82f73509308aa8a3a1fafc92e226f0 available from the official GitHub repository (https://github.com/harttle/liquidjs/commit/e2311dfd6e82f73509308aa8a3a1fafc92e226f0). Consult the GitHub security advisory GHSA-4rc3-7j7w-m548 for the specific release version number. If immediate patching is not feasible, implement template input validation to block nested blocks with identical names by parsing templates before rendering and rejecting structures where {% block X %} appears within another {% block X %}. Alternatively, enforce resource limits using Node.js --max-old-space-size flag (e.g., 512MB) combined with process monitoring to restart crashed instances, though this only mitigates impact and does not prevent the attack. For high-risk environments, disable user-submitted templates entirely or switch to a sandboxed template rendering service with isolated memory spaces per render operation. Note that memory limits will cause faster crashes but still result in service disruption; validation or patching are the only complete mitigations. Review application logs for abnormal memory growth patterns or OOM crashes as potential exploitation indicators.
FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote
Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t
Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete
Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc
An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner
Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi
Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin
The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic
Directory traversal vulnerability in lib/app/index.js in Geddy before 13.0.8 for Node.js allows remote attackers to read
Credential-harvesting malware compromised 84 versions of 42 TanStack npm packages on 2026-05-11 via chained GitHub Actio
Eval injection vulnerability in index.js in the syntax-error package before 1.1.1 for Node.js 0.10.x, as used in IBM Rat
The HTTP server in Node.js 0.10.x before 0.10.21 and 0.8.x before 0.8.26 allows remote attackers to cause a denial of se
Same weakness CWE-674 – Uncontrolled Recursion
View allSame technique Denial Of Service
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-28886
GHSA-4rc3-7j7w-m548