CVE-2026-30952: 
JavaScript vulnerability analysis and mitigation

Overview

CVE-2026-30952 is a path traversal vulnerability in liquidjs, a Shopify/GitHub Pages compatible Liquid template engine for Node.js. The flaw allows arbitrary file reads via absolute paths through the layout, render, and include tags — either as string literals or via Liquid variables (the latter requires dynamicPartials: true, which is the default). All versions prior to 10.25.0 are affected. The vulnerability was initially reported on December 30, 2025, publicly disclosed on March 8, 2026, and carries a CVSS v3.1 score of 7.5 (High) and a CVSS v4.0 score of 8.7 (High) (Github Advisory, GitHub Security Advisory).

Technical details

The root cause (CWE-22) resides in the candidates() generator function in src/fs/loader.ts. While the primary file lookup paths correctly apply enforceRoot and this.contains(dir, filepath) boundary checks, the fs.fallback block — which uses require.resolve() as a fallback mechanism — yields resolved file paths without verifying they fall within the configured root directories. This means an attacker who can supply user-controlled input to {% include %}, {% render %}, or {% layout %} tags can specify paths like ../../../etc/passwd (relative traversal with dynamicPartials: true) or absolute paths like /etc/passwd (string literals, exploitable even with dynamicPartials: false). The fix in PR #855 adds an isAllowed() helper that enforces the same root-directory containment check on fallback-resolved paths (GitHub Security Advisory, Fix PR #855, Commit 3cd024d).

Impact

Successful exploitation allows an unauthenticated attacker to read arbitrary files from the server's filesystem, including sensitive files such as /etc/passwd, application configuration files, private keys, or environment files containing credentials. The impact is limited to confidentiality — there is no integrity or availability impact — but the ability to read arbitrary files can facilitate lateral movement, credential theft, and further compromise of the hosting environment. Applications that allow end users to control template content or specify file paths as Liquid variables are at highest risk, and the vulnerability is noted to affect several well-known downstream open-source projects (Github Advisory, GitHub PR #851).

Exploitability

A public proof-of-concept exploit written in JavaScript is available on GitHub, demonstrating file reads (e.g., /etc/passwd) via Liquid().parseAndRender() with path traversal payloads (PoC Repository). As of the time of reporting, there is no evidence of active in-the-wild exploitation. The EPSS score is approximately 0.063% (low probability of near-term exploitation). The vulnerability is not listed in the CISA KEV catalog. No specific threat actor attribution has been made.

Exploitation steps

  1. Identify a target application: Find a Node.js application using liquidjs < 10.25.0 that allows user-controlled input to reach {% include %}, {% render %}, or {% layout %} template tags, or that exposes a Liquid variable used as a file path.
  2. Determine the attack vector: If dynamicPartials: true (the default), supply a path traversal string as a Liquid variable (e.g., page = '../../../etc/passwd'). If dynamicPartials: false, use an unquoted absolute path directly in a tag (e.g., {% render /etc/passwd %}).
  3. Craft the payload: Construct a Liquid template or variable value that references a sensitive file outside the configured root directory. Example PoC:
const { Liquid } = require('liquidjs');
const e = new Liquid({ root: ['/tmp'], partials: ['/tmp'], dynamicPartials: true });
e.parseAndRender('{% include page %}', { page: '../../../etc/passwd' })
  .then(o => console.log(o.slice(0, 500)));
  1. Submit the payload: Deliver the crafted template or variable value to the vulnerable application endpoint (e.g., via a form, API parameter, or template editor).
  2. Retrieve file contents: The application renders the template, the fallback resolver resolves the path outside the root directory without boundary checks, and the file contents are returned in the rendered output (GitHub PR #851, PoC Repository).

Indicators of compromise

  • Logs: Application logs showing template rendering requests with file paths containing ../ sequences or absolute paths (e.g., /etc/passwd, /etc/shadow, /.env) in Liquid tag parameters or variable values.
  • File System: Unexpected access timestamps on sensitive system files (e.g., /etc/passwd, /etc/hosts, application .env or config files) correlating with web request times.
  • Network: Outbound HTTP responses containing content characteristic of system files (e.g., Unix passwd file format with colon-delimited fields) returned from template rendering endpoints.
  • Process: Node.js process file descriptor activity showing reads of files outside the configured liquidjs root or partials directories.

Mitigation and workarounds

Upgrade liquidjs to version 10.25.0 or later, which applies the fix from PR #855 (commit 3cd024d) that enforces root-directory containment checks on fallback-resolved paths (Github Advisory). If immediate patching is not possible, two workarounds are available: (1) manually patch the dist/liquid.node.js file at build time to add root-directory checks in the fallback block, or (2) override the fs option when instantiating Liquid with a custom implementation that sets fallback: () => undefined to disable the fallback mechanism entirely. Additionally, restrict user control over template content and file path inputs, and implement strict input validation to reject absolute paths and traversal sequences in tag arguments (GitHub Security Advisory).

Community reactions

The vulnerability was discovered independently by multiple researchers: caplanmaor filed the initial private report on December 30, 2025; MorielHarush submitted a public PR and PoC on February 17, 2026; ByamB4 expanded the scope to all three affected tags; and peaktwilight independently identified a second attack vector (absolute paths with dynamicPartials: false). A public discussion in PR #851 included a notable debate about CVE credit attribution among the researchers. The maintainer (harttle) responded quickly, merging the fix and releasing version 10.25.0 on March 7, 2026 — the same day the fix was finalized. Researchers noted that the vulnerability affects several well-known downstream open-source projects with large user bases, though specific projects were not publicly named pending their own patching (GitHub PR #851).

Additional resources


Source: This report was generated using AI

Related JavaScript vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-966w-4vpf-p7hcHIGH7.1
  • JavaScript logoJavaScript
  • @aloma.io/aloma
NoYesOct 25, 2026
GHSA-mrwp-f3q8-3g9pMEDIUM5.9
  • JavaScript logoJavaScript
  • follow-redirects
NoYesOct 25, 2026
GHSA-hfm3-x63j-7fxpHIGHN/A
  • JavaScript logoJavaScript
  • @payload-auth-link/migrate
NoYesOct 25, 2026
GHSA-6244-vvv9-76f5MEDIUMN/A
  • JavaScript logoJavaScript
  • @payload-auth-link/plugin
NoYesOct 25, 2026
GHSA-q53q-qg98-p9jfHIGHN/A
  • JavaScript logoJavaScript
  • llm-fw
NoYesOct 25, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management