Handlebars: JavaScript Injection via Own Property Check Bypass
## 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 sourcePublished 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"
}