GitHub Advisoryadvisory · npm

GHSA-wmfp-5q7x-987x liquidjs has a path traversal fallback vulnerability

HIGH Published 2026-03-10 Modified 2026-03-12 Official page ↗
Aliases
CVE-2026-30952
Ecosystem
npm
Packages
liquidjs
Severity
High
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weaknesses
CWE-22
Reviewed
2026-03-10

Affected packages

• npm liquidjs: < 10.25.0 → fixed in 10.25.0

Description

Impact The `layout`, `render`, and `include` tags allow arbitrary file access via absolute paths (either as string literals or through Liquid variables, the latter require `dynamicPartials: true`, which is the default). This poses a security risk when malicious users are allowed to control the template content or specify the filepath to be included as a Liquid variable. Patches The root cause is LiquidJS allows `require.resolve()` as fallback but doesn't limit the directories it can resolve to. The issue is fixed via #855 and published version 10.25.0 on npm. Workarounds Change the files in build time In build time, through Shell script or Webpack `string-replace-loader`, change the file content of correxponding file (depending on your package `type`, for CommonJS it's `dist/liquid.node.js`) under `dist/`, ```diff if (fs.fallback !== undefined) { const filepath = fs.fallback(file) • if (filepath !== undefined) yield filepath + if (filepath !== undefined) { + for (const dir of dirs) { + if (!enforceRoot || this.contains(dir, filepath)) { + yield filepath + break + } + } } } ``` Overriding by `fs` LiquidJS option Adding a `fs` option to override the default `fs` implementation: ```javascript const { statSync, readFileSync, promises: { stat, readFile } } = require('fs') const { resolve, extname, dirname, sep } = require('path') const fs = { exists: async (fp) => { try { await stat(fp); return true; } catch { return false } }, existsSync: (fp) => { try { statSync(fp); return true } catch { return false } }, resolve: (root, file, ext) => resolve(root, file + (extname(file) ? '' : ext)), contains: (root, file) => { const r = resolve(root) return file.startsWith(r.endsWith(sep) ? r : r + sep) }, readFile: (fp) => readFile(fp, 'utf8'), readFileSync: (fp) => readFileSync(fp, 'utf8'), fallback: () => undefined, dirname, sep }; const engine = new Liquid({ fs }) ``` References Discussions: https://github.com/harttle/liquidjs/pull/851 Code fix: https://github.com/harttle/liquidjs/pull/855

Related

Alias · CVE (1)

  • CVE-2026-30952 liquidjs is a Shopify / GitHub Pages compatible template engine in pure JavaScript. vulnerability

Weakness · CWE (1)

  • CWE-22 Improper Limitation of a Pathname to a Restricted Directory weakness

References