HighGHSA-9cx6-37pm-9jffCVE-2026-33939

Handlebars.js has Denial of Service via Malformed Decorator Syntax in Template Compilation

Published 27 Mar 2026 · Updated 10 Sept 2026

Affected package

Affected
>=4.0.0 <4.7.9
Fixed in
4.7.9
Update to the fixed version for your release line, for example:npm install handlebars@4.7.9

Details

Summary

When a Handlebars template contains decorator syntax referencing an unregistered decorator (e.g. {{*n}}), the compiled template calls lookupProperty(decorators, "n"), which returns undefined. The runtime then immediately invokes the result as a function, causing an unhandled TypeError: ... is not a function that crashes the Node.js process. Any application that compiles user-supplied templates without wrapping the call in a try/catch is vulnerable to a single-request Denial of Service.

Description

In lib/handlebars/compiler/javascript-compiler.js, the code generated for a decorator invocation looks like:

fn = lookupProperty(decorators, "n")(fn, props, container, options) || fn;

When "n" is not a registered decorator, lookupProperty(decorators, "n") returns undefined. The expression immediately attempts to call undefined as a function, producing:

TypeError: lookupProperty(...) is not a function

Because the error is thrown inside the compiled template function and is not caught by the runtime, it propagates up as an unhandled exception and — when not caught by the application — crashes the Node.js process.

This inconsistency is notable: references to unregistered helpers produce a clean "Missing helper: ..." error, while references to unregistered decorators cause a hard crash.

Attack scenario: An attacker submits {{*n}} as template content to any endpoint that calls Handlebars.compile(userInput)(). Each request crashes the server process; with process managers that auto-restart (PM2, systemd), repeated submissions create a persistent DoS.

Proof of Concept

const Handlebars = require('handlebars'); // Handlebars 4.7.8, Node.js v22.x

// Any of these payloads crash the process
Handlebars.compile('{{*n}}')({});
Handlebars.compile('{{*decorator}}')({});
Handlebars.compile('{{*constructor}}')({});

Expected crash output:

TypeError: lookupProperty(...) is not a function
    at Function.eval [as decorator] (eval at compile (...javascript-compiler.js:134:36))

Workarounds

  • Wrap compilation and rendering in try/catch:
  try {
    const result = Handlebars.compile(userInput)(context);
    res.send(result);
  } catch (err) {
    res.status(400).send('Invalid template');
  }
  • Validate template input before passing it to compile(). Reject templates containing decorator syntax ({{*...}}) if decorators are not used in your application.
  • Use the pre-compilation workflow: compile templates at build time and serve only pre-compiled templates; do not call compile() at request time.

Is GHSA-9cx6-37pm-9jff in any of your client repos?

Checking one package by hand is fine. Checking it across forty client projects is not. NPMscan reads the lockfiles in every GitHub repo you pick, finds each project with an affected handlebars, and gives you the command that fixes it.

References

Advisory data from OSV.dev (opens OSV.dev in a new tab), refreshed daily.