---
id: 2026-10-handlebars-compile-ast-javascript-injection-bypass-cve-batch
title: "Handlebars (41.5M weekly downloads): the March AST validation is bypassed, so an object passed to compile() still runs attacker JavaScript (CVE-2026-106446, 9.8), and the prototype deny list misses Function.prototype.constructor (CVE-2026-106445, 9.2); fixed 4.7.10; exploitable only where untrusted objects reach compile() or allowProtoMethodsByDefault is on"
date_disclosed: 2026-10-05
last_updated: 2026-10-09
severity: high
status: patched
ecosystems: [npm, nodejs, templating]
tools_affected: [handlebars, "Node apps and email/report generators that compile templates from request data", "any build that precompiles templates from untrusted input"]
tags: [cve, code-injection, template-injection, incomplete-fix, prototype-pollution, npm-core-library]
---

## TL;DR
**Handlebars** (41,542,245 npm downloads in the week to 2026-10-07) fixed two JavaScript-injection bugs in **4.7.10** on 2026-10-05. **CVE-2026-106446** (CVSS 9.8): `Handlebars.compile()` and `precompile()` accept a pre-parsed AST object as well as a template string; version 4.7.9 (2026-03-26, for CVE-2026-33937) added validation for only three node types, and the compiler still writes other AST fields such as `Program.blockParams.length` straight into generated JavaScript. An application that passes an untrusted object, for example a parsed JSON body, to `compile()` runs attacker code when the template renders; with `precompile()` the code is embedded in the output and runs wherever that is loaded. **CVE-2026-106445** (CVSS 4.0 9.2): `lookupProperty` returns `Function.prototype.constructor` before the prototype-access deny list runs, so a template rendered with `allowProtoMethodsByDefault: true` and a function in its context can reach the `Function` constructor. Applications that only ever pass template strings to `compile()` and keep the default proto settings are not affected by either. Upgrade to 4.7.10 anyway; the scanner noise alone will force it.

## What happened
The maintainers published both advisories on the repository on 2026-10-05, GitHub as CNA issued the CVEs on 2026-10-06, and the reviewed-critical npm listing carried both on 2026-10-08.

**CVE-2026-106446 (GHSA-8r5x-fm3f-whwj), 9.8, CWE-94/843, 4.0.0 to 4.7.9, fixed 4.7.10.** This is the second incomplete-fix round for the same entry point. In March, CVE-2026-33937 (9.8) showed that a `NumberLiteral.value` in a crafted AST was emitted unquoted into generated code, and CVE-2026-33940 (8.1) showed a dynamic partial lookup could hand an object to `env.compile()`; 4.7.9 added a validator for `PathExpression`, `NumberLiteral` and `BooleanLiteral` nodes. The new advisory lists the fields the validator never looked at: `Program.blockParams.length` works with default options; `depth`, a non-string `StringLiteral.value` and a non-string `PathExpression.original` need `stringParams: true`. Sixteen reporters are credited across three variants, including Team Atlanta and Shopify's open-source security team, which is the pattern of a bug class being fuzzed in parallel once the first instance is public.

**CVE-2026-106445 (GHSA-p8wg-vrv2-v86f), 9.2 (CVSS 4.0, `AT:P`), CWE-184/1289, 4.0.0 to 4.7.9, fixed 4.7.10.** The runtime's prototype-access protection uses a deny list, but `constructor` is an own property of `Function.prototype`, so the lookup succeeds before the list is consulted. The precondition is a template the attacker controls, rendered with `allowProtoMethodsByDefault: true`, and a function reachable from the context. The vendor's workaround is simply not to set that option for untrusted templates. Reporter brandon-t-elliott.

**Who is actually exposed.** The 9.8 is an unauthenticated network score for the shape "server passes a request body to `compile()`". Most generated apps compile string templates from their own source tree, where neither bug applies. The exposed shapes are: a template editor or notification service that stores user-supplied templates and compiles them per request; an API that accepts JSON and forwards it to `compile()` without a type check; or a build pipeline that precompiles templates from untrusted input. Filed **high** rather than critical for that reason, with both registry scores shown.

## Am I affected?
```bash
npm ls handlebars 2>/dev/null                       # anything below 4.7.10
grep -rn "Handlebars.compile\|Handlebars.precompile\|allowProtoMethodsByDefault\|stringParams" --include=*.js --include=*.ts . | grep -v node_modules | head
```
If every `compile()` call site receives a string literal or a string read from your own files, you are not exploitable on any version. If any call site can receive a parsed object, or you set `allowProtoMethodsByDefault: true` for user templates, you are.

## If you are affected
1. Upgrade `handlebars` to **4.7.10 or later**; regenerate the lockfile.
2. Add `typeof template === 'string'` before every `compile()` / `precompile()` call, and reject anything else. The vendor lists this as the workaround for both March and October bugs.
3. Servers that only render precompiled templates should import `handlebars/runtime`, where `compile()` does not exist.
4. Do not set `allowProtoMethodsByDefault: true` for templates you did not write.
5. If an untrusted object could have reached `compile()` on an affected version, treat it as code execution and follow [if-your-webapp-was-compromised.md](../playbooks/if-your-webapp-was-compromised.md).

## Prevention
Template engines are compilers; give them strings, never structures from the network ([auditing-a-vibe-coded-repo.md](../playbooks/auditing-a-vibe-coded-repo.md)). Keep the runtime-only build in production, and let `npm audit` block the deploy when a 9.8 lands in a library this widely installed ([npm-hardening.md](../prevention/npm-hardening.md)).

## Sources
- [GitHub Advisory Database — GHSA-8r5x-fm3f-whwj: Handlebars: JavaScript Injection via AST Type Confusion in compile (bypass of CVE-2026-33937)](https://github.com/advisories/GHSA-8r5x-fm3f-whwj) (fetched 2026-10-09; CVE-2026-106446, 9.8 `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`, 4.0.0 to 4.7.9 → 4.7.10, repository 2026-10-05, NVD 2026-10-06, database 2026-10-08, unchecked fields, workarounds, PR #2185, credits)
- [GitHub Advisory Database — GHSA-p8wg-vrv2-v86f: Handlebars: JavaScript Injection via Own Property Check Bypass](https://github.com/advisories/GHSA-p8wg-vrv2-v86f) (CVE-2026-106445, CVSS 4.0 9.2 `AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H`, 4.0.0 to 4.7.9 → 4.7.10, `allowProtoMethodsByDefault` precondition and workaround, reporter brandon-t-elliott)
- [CVE Services — CVE-2026-106446](https://cveawg.mitre.org/api/cve/CVE-2026-106446) and [CVE-2026-106445](https://cveawg.mitre.org/api/cve/CVE-2026-106445) (GitHub as CNA, published 2026-10-06; affected `>= 4.0.0, < 4.7.10`)
- [GitHub Advisory Database — GHSA-2w6w-674q-4c4q (CVE-2026-33937)](https://github.com/advisories/GHSA-2w6w-674q-4c4q) and [GHSA-xhpv-hc6g-r9c6 (CVE-2026-33940)](https://github.com/advisories/GHSA-xhpv-hc6g-r9c6) (the March 2026 pair fixed in 4.7.9; repository 2026-03-26)
- [GitHub Advisory Database — reviewed critical npm listing](https://github.com/advisories?query=type%3Areviewed+ecosystem%3Anpm+severity%3Acritical) (both rows on 2026-10-08); `api.npmjs.org` weekly download point for `handlebars` (41,542,245, 2026-10-01 to 10-07) and `npm view handlebars version` (4.7.10), checked 2026-10-09
