{"id":"CVE-2026-30952","aliases":["GHSA-wmfp-5q7x-987x"],"url":"https://o3.security/vulnerability/CVE-2026-30952","summary":"liquidjs has a path traversal fallback vulnerability","details":"### Impact\nThe `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.\n\n### Patches\nThe 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](https://github.com/harttle/liquidjs/pull/855) and published version 10.25.0 on npm.\n\n### Workarounds\n#### Change the files in build time\nIn 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/`, \n\n```diff\n  if (fs.fallback !== undefined) {\n    const filepath = fs.fallback(file)\n-   if (filepath !== undefined) yield filepath\n+   if (filepath !== undefined) {\n+     for (const dir of dirs) {\n+       if (!enforceRoot || this.contains(dir, filepath)) {\n+         yield filepath\n+         break\n+       }\n+     }\n    }\n  }\n```\n\n#### Overriding by `fs` LiquidJS option\nAdding a [`fs` option](https://liquidjs.com/api/interfaces/FS.html) to override the [default `fs` implementation](https://github.com/harttle/liquidjs/blob/1b85fdaa9c535021f7030a239a64003af26d31b5/src/fs/fs-impl.ts#L36-L40):\n\n```javascript\nconst { statSync, readFileSync, promises: { stat, readFile } } = require('fs')\nconst { resolve, extname, dirname, sep } = require('path')\n\nconst fs = {\n    exists: async (fp) => { try { await stat(fp); return true; } catch { return false } },\n    existsSync: (fp) => { try { statSync(fp); return true } catch { return false } },\n    resolve: (root, file, ext) => resolve(root, file + (extname(file) ? '' : ext)),\n    contains: (root, file) => {\n        const r = resolve(root)\n        return file.startsWith(r.endsWith(sep) ? r : r + sep)\n    },\n    readFile: (fp) => readFile(fp, 'utf8'),\n    readFileSync: (fp) => readFileSync(fp, 'utf8'),\n    fallback: () => undefined,\n    dirname,\n    sep\n};\n\nconst engine = new Liquid({ fs })\n```\n\n### References\nDiscussions: https://github.com/harttle/liquidjs/pull/851\nCode fix: https://github.com/harttle/liquidjs/pull/855","published":"2026-03-10T20:25:20.176Z","modified":"2026-08-12T03:51:21.727432763Z","cvss":null,"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"liquidjs","fixedVersion":"10.25.0"}],"fix":{"url":"https://github.com/harttle/liquidjs/commit/3cd024d652dc883c46307581e979fe32302adbac","label":"harttle/liquidjs@3cd024d"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/30xxx/CVE-2026-30952.json"},{"type":"ADVISORY","url":"https://github.com/harttle/liquidjs/security/advisories/GHSA-wmfp-5q7x-987x"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-30952"},{"type":"FIX","url":"https://github.com/harttle/liquidjs/commit/3cd024d652dc883c46307581e979fe32302adbac"},{"type":"FIX","url":"https://github.com/harttle/liquidjs/pull/851"},{"type":"FIX","url":"https://github.com/harttle/liquidjs/pull/855"},{"type":"PACKAGE","url":"https://github.com/harttle/liquidjs"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:21.727432763Z"}}