GHSA-9cx6-37pm-9jff

Suggest an improvement
Source
https://github.com/advisories/GHSA-9cx6-37pm-9jff
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-9cx6-37pm-9jff/GHSA-9cx6-37pm-9jff.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-9cx6-37pm-9jff
Aliases
Downstream
Related
Published
2026-03-27T18:21:15Z
Modified
2026-03-30T20:21:06.588548Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
Handlebars.js has Denial of Service via Malformed Decorator Syntax in Template Compilation
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.
Database specific
{
    "github_reviewed": true,
    "nvd_published_at": "2026-03-27T22:16:20Z",
    "cwe_ids": [
        "CWE-754"
    ],
    "github_reviewed_at": "2026-03-27T18:21:15Z",
    "severity": "HIGH"
}
References

Affected packages

npm / handlebars

Package

Affected ranges

Type
SEMVER
Events
Introduced
4.0.0
Fixed
4.7.9

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-9cx6-37pm-9jff/GHSA-9cx6-37pm-9jff.json"
last_known_affected_version_range
"<= 4.7.8"