Skip to content
Heist/ feed
Back to feed
CriticalFix available

Handlebars: JavaScript Injection via Own Property Check Bypass

Type: advisorySources: OSV

## 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: ```js 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: ```js 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: 1. 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). 2. Access constructor vi

Products
handlebars
Affected versions
Not specified
Fixed versions
4.7.10
CVEs
CVE-2026-106445
Confirmed fixed CVEs
Not specified
Updated in this feed
30 Oct 2026, 06:37 UTC · Revision 1

Source evidence

Handlebars: JavaScript Injection via Own Property Check Bypass

core-osv · Original source

Published 8 Oct 2026, 17:52 UTC

## 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: ```js 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: ```js 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: 1. 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). 2. Access constructor vi

osv:GHSA-p8wg-vrv2-v86f

handlebars

Fixed versions: 4.7.10

Complete source record
{
  "id": "4681ca07261e1e018b63f14a62e5a2beefc565dde90d25ec31d29df884906568",
  "url": "https://osv.dev/vulnerability/GHSA-p8wg-vrv2-v86f",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "Handlebars: JavaScript Injection via Own Property Check Bypass",
    "summary": "## 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: ```js 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: ```js 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: 1. 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). 2. Access constructor vi",
    "evidence": "osv:GHSA-p8wg-vrv2-v86f",
    "products": [
      {
        "name": {
          "value": "handlebars",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "4.7.10",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": []
      }
    ],
    "severity": "critical",
    "structured": {
      "id": "GHSA-p8wg-vrv2-v86f",
      "aliases": [
        "CVE-2026-106445",
        "GHSA-p8wg-vrv2-v86f"
      ],
      "metrics": [
        {
          "type": "CVSS_V4",
          "score": "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"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "SEMVER",
              "events": [
                {
                  "introduced": "3.0.0"
                },
                {
                  "fixed": "4.7.10"
                }
              ]
            }
          ],
          "package": {
            "name": "handlebars",
            "purl": "pkg:npm/handlebars",
            "ecosystem": "npm"
          },
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-p8wg-vrv2-v86f/GHSA-p8wg-vrv2-v86f.json",
            "last_known_affected_version_range": "<= 4.7.9"
          }
        }
      ],
      "modified": "2026-10-12T14:45:06.527043960Z",
      "provider": "osv",
      "references": [
        "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-p8wg-vrv2-v86f",
        "https://nvd.nist.gov/vuln/detail/CVE-2026-106445",
        "https://github.com/handlebars-lang/handlebars.js/pull/2185",
        "https://github.com/handlebars-lang/handlebars.js/commit/ceec388abe1d1aac8f6369860d5f390fa71ef4fa",
        "https://github.com/handlebars-lang/handlebars.js",
        "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.10"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-1289",
          "CWE-184"
        ],
        "severity": "CRITICAL",
        "github_reviewed": true,
        "nvd_published_at": "2026-10-06T20:17:26Z",
        "github_reviewed_at": "2026-10-08T17:52:26Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-106445",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/database_specific/severity"
  },
  "source_id": "core-osv",
  "document_id": 299961,
  "published_at": "2026-10-08T17:52:26+00:00"
}