Skip to main content

Node.js EUVDEUVD-2026-20554

| CVE-2026-34166 MEDIUM
Uncontrolled Resource Consumption (CWE-400)
2026-04-08 https://github.com/harttle/liquidjs GHSA-mmg9-6m6j-jqqx
5.3
CVSS 3.1 · NVD
View

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A06:2025 - Insecure Design
ASVS 5.0
V2 - Validation and Business Logic Resource consumption limits
MITRE ATT&CK

1 technique across 1 tactic: Impact

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Impact

While the CVSS score of 3.7 (low severity) reflects limited scope and availability-only impact, the real-world risk is significantly elevated for specific deployment patterns. The CVSS vector (AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L) indicates network-accessible, no-privilege-required exploitation with high attack complexity, which appears initially limiting; however, the high complexity requirement reflects only the need to craft specific template patterns-not a true technical barrier for any attacker with template authoring capability. The absence of active exploitation (KEV status not indicated) and the non-default configuration requirement (memoryLimit must be explicitly enabled) reduce immediate threat level. However, organizations that intentionally deploy LiquidJS with memoryLimit enabled specifically to mitigate template-injection DoS risks face a critical gap: their primary defense mechanism is demonstrably ineffective against this attack class with 2,500x amplification factors documented in proof-of-concept. The availability impact is severe for affected deployments-complete process crashes and multi-tenant resource exhaustion are realistic outcomes. This represents a case where low CVSS-scored vulnerabilities warrant immediate patching when deployed in threat models matching the vulnerability's assumptions (untrusted template input + resource limits).

Exploit ScenarioDerived

An attacker with ability to author templates in a multi-tenant LiquidJS deployment protected by memoryLimit (e.g., custom user template SaaS) crafts a template assigning a 5,000-character string of repeated characters (e.g., "aaaa..."), then uses the replace filter to replace each character with another 5,000-character string, producing a 25-million-character output. The memory limiter charges only ~10 KB (sum of input lengths) while the operation actually allocates ~25 MB, bypassing a 10 MB limit by 2.5x. Repeated invocations or larger input patterns can exhaust Node.js heap memory, crashing the process and denying service to all co-tenant users. No public exploit code is documented beyond the proof-of-concept in the advisory.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.0% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Apr 08, 2026

The replace filter in LiquidJS (Node.js npm package) fails to correctly account for memory usage when memoryLimit is enabled, allowing remote attackers to bypass DoS protections with approximately 2,500x memory amplification by crafting templates where the replace operation produces quadratically larger output than the charged memory cost. Deployments with memoryLimit explicitly configured to protect against untrusted template input can suffer out-of-memory crashes; patch available in v10.25.3.

RemediationAI

Vendor-released patch: upgrade to liquidjs v10.25.3 or later. The fix modifies the replace filter to calculate actual output size using const outputSize = str.length + (parts.length - 1) * (replacement.length - pattern.length) before charging the memory limiter, ensuring the memory cost accurately reflects the operation's actual resource consumption. Organizations deploying LiquidJS with memoryLimit enabled should prioritize this update to restore intended DoS protections. Details available in the vendor advisory at https://github.com/harttle/liquidjs/security/advisories/GHSA-mmg9-6m6j-jqqx and the patched commit abc058be0f33d6372cd2216f4945183167abeb25 at https://github.com/harttle/liquidjs/releases/tag/v10.25.3.

Affected VersionsENISA

liquidjs < 10.25.3

Affected Packages

npm liquidjs fix: 10.25.3

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in npm

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

LiquidJS is a Liquid template engine implementation for JavaScript/Node.js used in multi-tenant SaaS platforms to process user-supplied templates safely. The vulnerability exists in the replace filter (src/filters/string.ts), which uses a memory accounting system (Limiter class in src/util/limiter.ts) to enforce a configurable memoryLimit and prevent denial-of-service attacks. The filter charges only the sum of input string, pattern, and replacement lengths to the memory limiter (str.length + pattern.length + replacement.length bytes), but the actual operation str.split(pattern).join(replacement) produces output sized as (number_of_occurrences × replacement.length) + non_matching_characters. When a pattern occurs repeatedly throughout the input string, the output size grows quadratically while memory charges remain linear, creating a severe mismatch. This is a CWE-400 (Uncontrolled Resource Consumption) vulnerability rooted in incorrect resource accounting logic. The memoryLimit option defaults to Infinity and must be explicitly enabled; only deployments using this feature are vulnerable.

Know when Node.js gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

NVD PRIMARY 5.3 MEDIUM
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

Primary rating from NVD · only source for this CVE.

CVSS VectorNVD

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
Low

Lifecycle Timeline

4
Severity Changed
Jul 24, 2026 - 22:23 NVD
LOW MEDIUM
CVSS changed
Jul 24, 2026 - 22:23 NVD
3.7 (LOW) 5.3 (MEDIUM)
Patch released
Apr 08, 2026 - 15:16 nvd
Patch available
CVE Published
Apr 08, 2026 - 15:00 nvd
LOW 3.7

DescriptionNVD

Summary

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:

typescript
export function replace (this: FilterImpl, v: string, pattern: string, replacement: string) {
  const str = stringify(v)
  pattern = stringify(pattern)
  replacement = stringify(replacement)
  this.context.memoryLimit.use(str.length + pattern.length + replacement.length)  // BUG: accounts for inputs, not output
  return str.split(pattern).join(replacement)  // actual output can be quadratically larger
}

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:

(number_of_occurrences * replacement.length) + non_matching_characters

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 = 10,001 bytes are charged to the limiter.

The Limiter class at src/util/limiter.ts:3-22 is a simple accumulator - it only checks at the time use() is called and has no post-hoc validation of actual memory allocated.

The memoryLimit option defaults to Infinity (src/liquid-options.ts:198), so this only affects deployments that explicitly enable memory limiting to protect against untrusted template input.

PoC

javascript
const { Liquid } = require('liquidjs');

// User explicitly enables memoryLimit for DoS protection (10MB)
const engine = new Liquid({ memoryLimit: 1e7 });

const inputLen = 5000;
const aStr = 'a'.repeat(inputLen);
const bStr = 'b'.repeat(inputLen);

// Template that should be blocked by 10MB memory limit
const tpl = engine.parse(
  `{%- assign s = "${aStr}" -%}` +
  `{%- assign r = "${bStr}" -%}` +
  `{{ s | replace: "a", r }}`
);

// This should throw "memory alloc limit exceeded" but succeeds
const result = engine.renderSync(tpl);

console.log('Memory limit: 10,000,000 bytes');
console.log('Memory charged:', 10001, 'bytes');
console.log('Actual output:', result.length, 'bytes');  // 25,000,000 bytes
console.log('Amplification:', Math.round(result.length / 10001) + 'x');
// Output: Amplification: 2500x - completely bypasses the 10MB limit

Impact

Users who deploy LiquidJS with memoryLimit enabled to process untrusted templates (e.g., multi-tenant SaaS platforms allowing custom templates) are not protected against memory exhaustion via the replace filter. An attacker who can author templates can allocate ~2,500x more memory than the configured limit allows, potentially causing:

  • Node.js process out-of-memory crashes
  • Denial of service for co-tenant users on the same process
  • Resource exhaustion on the hosting infrastructure

The impact is limited to availability (no confidentiality or integrity impact), and requires both non-default configuration (memoryLimit enabled) and template authoring access.

Recommended Fix

Account for the actual output size in the memory limiter by calculating the number of occurrences:

typescript
export function replace (this: FilterImpl, v: string, pattern: string, replacement: string) {
  const str = stringify(v)
  pattern = stringify(pattern)
  replacement = stringify(replacement)
  const parts = str.split(pattern)
  const outputSize = str.length + (parts.length - 1) * (replacement.length - pattern.length)
  this.context.memoryLimit.use(outputSize)
  return parts.join(replacement)
}

This computes the exact output size: the original string length plus, for each occurrence, the difference between the replacement and pattern lengths. The split() result is reused to avoid computing it twice.

Threat Intelligence Overview

EPSS Score
0.0%
Top 89% exploitable
Threat Intel
1
1 source
Data Quality
6 sources
Confidence: High
EUVD NVD CVE.org EPSS Vulnrichment GHSA GitHub

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS 0.0%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation
Poc
System Exposure· derived
Open
Automatable
No
Human Impact· derived
Medium
Technical Impact
Partial

Priority Score

26
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +26
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.

Related Vulnerabilities in LiquidJS Path Traversal and Memory Issues

View all 3
CVE-2025-68613 HIGH POC
8.8 Dec 19

Remote code execution in n8n's workflow expression evaluation system allows an authenticated user with workflow creation

CVE-2024-41713 CRITICAL POC
9.1 Oct 21

A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could

CVE-2024-55591 CRITICAL POC
9.8 Jan 14

FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote

CVE-2023-44487 HIGH POC
7.5 Oct 10

Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour

CVE-2014-7205 CRITICAL POC
10.0 Oct 08

Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t

CVE-2025-59528 CRITICAL POC
10.0 Sep 22

Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete

CVE-2017-14849 HIGH POC
7.5 Sep 28

Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc

CVE-2017-5941 CRITICAL POC
9.8 Feb 09

An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2014-3744 HIGH POC
7.5 Oct 23

Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi

CVE-2014-9566 HIGH POC
7.5 Mar 10

Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin

CVE-2013-4660 MEDIUM POC
6.8 Jun 28

The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy