Repository navigation
chore(deps): update dependency handlebars to v4.7.10 [security] - #1488
Open
renovate-bot wants to merge 1 commit into
Open
renovate-bot wants to merge 1 commit into
renovate-bot wants to merge 1 commit into
Conversation
Collaborator
|
/gcbrun |
There was a problem hiding this comment.
Code Review
This pull request updates the handlebars dependency from version 4.7.8 to 4.7.9 in the package-lock.json files of both the nodejs-cloud-run-hello-world and nodejs-hello-world projects. There are no review comments, and I have no feedback to provide.
renovate-bot
force-pushed
the
renovate/npm-handlebars-vulnerability
branch
from
October 11, 2026 22:24
39a0209 to
90e9fe4
Compare
Collaborator
|
/gcbrun |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
4.7.8→4.7.10Handlebars.js has JavaScript Injection via AST Type Confusion
CVE-2026-33937 / GHSA-2w6w-674q-4c4q
More information
Details
Summary
Handlebars.compile()accepts a pre-parsed AST object in addition to a template string. Thevaluefield of aNumberLiteralAST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST tocompile()can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.Description
Handlebars.compile()accepts either a template string or a pre-parsed AST. When an AST is supplied, the JavaScript code generator inlib/handlebars/compiler/javascript-compiler.jsemitsNumberLiteralvalues verbatim:Because the value is not wrapped in quotes or otherwise sanitized, passing a string such as
{},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() //as thevalueof aNumberLiteralcauses the generatedeval-ed code to break out of its intended context and execute arbitrary commands.Any endpoint that deserializes user-controlled JSON and passes the result directly to
Handlebars.compile()is exploitable.Proof of Concept
Server-side Express application that passes
req.body.texttoHandlebars.compile():The response body will contain the output of the
idcommand executed on the server.Workarounds
Handlebars.compile(): ensure the argument is always astring, never a plain object or JSON-deserialized value.handlebars/runtime) on the server if templates are pre-compiled at build time;compile()will be unavailable.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has JavaScript Injection via AST Type Confusion by tampering @partial-block
CVE-2026-33938 / GHSA-3mfm-83xf-c92r
More information
Details
Summary
The
@partial-blockspecial variable is stored in the template data context and is reachable and mutable from within a template via helpers that accept arbitrary objects. When a helper overwrites@partial-blockwith a crafted Handlebars AST, a subsequent invocation of{{> @partial-block}}compiles and executes that AST, enabling arbitrary JavaScript execution on the server.Description
Handlebars stores
@partial-blockin thedataframe that is accessible to templates. In nested contexts, a parent frame's@partial-blockis reachable as@_parent.partial-block. Because the data frame is a mutable object, any registered helper that accepts an object reference and assigns properties to it can overwrite@partial-blockwith an attacker-controlled value.When
{{> @partial-block}}is subsequently evaluated,invokePartialreceives the crafted object. The runtime, finding an object that is not a compiled function, falls back to dynamically compiling the value viaenv.compile(). If that value is a well-formed Handlebars AST containing injected code, the injected JavaScript runs in the server process.The
handlebars-helpersnpm package (commonly used with Handlebars) includes several helpers such asmergethat can be used as the mutation primitive.Proof of Concept
Tested with Handlebars 4.7.8 and
handlebars-helpers:Workarounds
require('handlebars/runtime')). Thecompile()method is absent, eliminating the vulnerable fallback path.handlebars-helpers) in contexts where templates or context data can be influenced by untrusted input.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has Denial of Service via Malformed Decorator Syntax in Template Compilation
CVE-2026-33939 / GHSA-9cx6-37pm-9jff
More information
Details
Summary
When a Handlebars template contains decorator syntax referencing an unregistered decorator (e.g.
{{*n}}), the compiled template callslookupProperty(decorators, "n"), which returnsundefined. The runtime then immediately invokes the result as a function, causing an unhandledTypeError: ... is not a functionthat crashes the Node.js process. Any application that compiles user-supplied templates without wrapping the call in atry/catchis 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:When
"n"is not a registered decorator,lookupProperty(decorators, "n")returnsundefined. The expression immediately attempts to callundefinedas a function, producing: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 callsHandlebars.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
Expected crash output:
Workarounds
try/catch:compile(). Reject templates containing decorator syntax ({{*...}}) if decorators are not used in your application.compile()at request time.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has JavaScript Injection via AST Type Confusion when passing an object as dynamic partial
CVE-2026-33940 / GHSA-xhpv-hc6g-r9c6
More information
Details
Summary
A crafted object placed in the template context can bypass all conditional guards in
resolvePartial()and causeinvokePartial()to returnundefined. The Handlebars runtime then treats the unresolved partial as a source that needs to be compiled, passing the crafted object toenv.compile(). Because the object is a valid Handlebars AST containing injected code, the generated JavaScript executes arbitrary commands on the server. The attack requires the adversary to control a value that can be returned by a dynamic partial lookup.Description
The vulnerable code path spans two functions in
lib/handlebars/runtime.js:resolvePartial(): A crafted object withcall: truesatisfies the first branch condition (partial.call) and causes an early return of the original object itself, because none of the remaining conditionals (string check,options.partialslookup, etc.) match a plain object. The function returns the crafted object as-is.invokePartial(): WhenresolvePartialreturns a non-function object,invokePartialproducesundefined. The runtime interpretsundefinedas "partial not yet compiled" and callsenv.compile(partial, ...)wherepartialis the crafted AST object. The JavaScript code generator processes the AST and emits JavaScript containing the injected payload, which is then evaluated.Minimum prerequisites:
{{> (lookup . "key")}}or equivalent.In server-side rendering scenarios where templates process user-supplied context data, this enables full Remote Code Execution.
Proof of Concept
Workarounds
require('handlebars/runtime')). Withoutcompile(), the fallback compilation path ininvokePartialis unreachable.{{> (lookup ...)}}) when context data is user-controlled.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has JavaScript Injection in CLI Precompiler via Unescaped Names and Options
CVE-2026-33941 / GHSA-xjpj-3mr7-gcpf
More information
Details
Summary
The Handlebars CLI precompiler (
bin/handlebars/lib/precompiler.js) concatenates user-controlled strings — template file names and several CLI options — directly into the JavaScript it emits, without any escaping or sanitization. An attacker who can influence template filenames or CLI arguments can inject arbitrary JavaScript that executes when the generated bundle is loaded in Node.js or a browser.Description
lib/precompiler.jsgenerates JavaScript source by string-interpolating several values directly into the output. Four distinct injection points exist:1. Template name injection
template.nameis derived from the file system path. A filename containing"or'];breaks out of the string literal and injects arbitrary JavaScript.2. Namespace injection (
-n/--namespace)opts.namespaceis emitted as raw JavaScript. Anything after a;in the value becomes an additional JavaScript statement.3. CommonJS path injection (
-c/--commonjs)opts.commonjsis interpolated inside double quotes with no escaping, allowing"to close the string and inject further code.4. AMD path injection (
-h/--handlebarPath)opts.handlebarPathis interpolated inside single quotes, allowing'to close the array element.All four injection points result in code that executes when the generated bundle is
require()d or loaded in a browser.Proof of Concept
Template name vector (creates a file
pwnedon disk):Namespace vector:
CommonJS vector:
AMD vector:
Workarounds
",',;, etc.).Severity
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has a Property Access Validation Bypass in container.lookup
GHSA-442j-39wm-28r2
More information
Details
Summary
In
lib/handlebars/runtime.js, thecontainer.lookup()function usescontainer.lookupProperty()as a gate check to enforce prototype-access controls, but then discards the validated result and performs a second, unguarded property access (depths[i][name]). This Time-of-Check Time-of-Use (TOCTOU) pattern means the security check and the actual read are decoupled, and the raw access bypasses any sanitization thatlookupPropertymay perform.Only relevant when the compat compile option is enabled (
{compat: true}), which activatesdepthedLookupinlib/handlebars/compiler/javascript-compiler.js.Description
The vulnerable code in
lib/handlebars/runtime.js(lines 137–144):container.lookupProperty()(lines 119–136) enforceshasOwnPropertychecks andresultIsAllowed()prototype-access controls. However,container.lookup()only useslookupPropertyas a boolean gate — if the gate passes (result != null), it then performs an independent, rawdepths[i][name]access that circumvents any transformation or wrapped value thatlookupPropertymay have returned.Workarounds
{ compat: true }when rendering templates that include untrusted data.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has a Prototype Method Access Control Gap via Missing lookupSetter Blocklist Entry
GHSA-7rx3-28cr-v5wh
More information
Details
Summary
The prototype method blocklist in
lib/handlebars/internal/proto-access.jsblocksconstructor,__defineGetter__,__defineSetter__, and__lookupGetter__, but omits the symmetric__lookupSetter__. This omission is only exploitable when the non-default runtime optionallowProtoMethodsByDefault: trueis explicitly set — in that configuration__lookupSetter__becomes accessible while its counterparts remain blocked, creating an inconsistent security boundary.4.6.0is the version that introducedprotoAccessControland theallowProtoMethodsByDefaultruntime option.Description
In
lib/handlebars/internal/proto-access.js:All four legacy accessor helpers (
__defineGetter__,__defineSetter__,__lookupGetter__,__lookupSetter__) were involved in the exploit chain addressed by CVE-2021-23383. Three of the four were explicitly blocked;__lookupSetter__was left out.When
allowProtoMethodsByDefault: trueis set, any prototype method not present inmethodWhiteListis permitted by default. Because__lookupSetter__is absent from the list, it passes thecheckWhiteListcheck and is accessible in templates, while__lookupGetter__(its sibling) is correctly denied.Workarounds
allowProtoMethodsByDefault: true. The default configuration is not affected.allowProtoMethodsByDefaultmust be enabled, ensure templates do not reference__lookupSetter__through untrusted input.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars.js has Prototype Pollution Leading to XSS through Partial Template Injection
CVE-2026-33916 / GHSA-2qvq-rjwj-gvw9
More information
Details
Summary
resolvePartial()in the Handlebars runtime resolves partial names via a plain property lookup onoptions.partialswithout guarding against prototype-chain traversal. WhenObject.prototypehas been polluted with a string value whose key matches a partial reference in a template, the polluted string is used as the partial body and rendered without HTML escaping, resulting in reflected or stored XSS.Description
The root cause is in
lib/handlebars/runtime.jsinsideresolvePartial()andinvokePartial():hasOwnPropertyis never checked, so ifObject.prototypehas been seeded with a key whose name matches a partial reference in the template (e.g.widget), the lookup succeeds and the polluted string is returned. The runtime emits a prototype-access warning, but the partial is still resolved and its content is inserted into the rendered output unescaped. This contradicts the documented security model and is distinct from CVE-2021-23369 and CVE-2021-23383, which addressed data property access rather than partial template resolution.Prerequisites for exploitation:
qs,minimist, orany querystring/JSON merge sink).
Proof of Concept
Workarounds
Object.freeze(Object.prototype)early in application startup to prevent prototype pollution. Note: this may break other libraries.handlebars/runtime), which does not compile templates and reduces the attack surface.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars: JavaScript Injection via AST Type Confusion in compile (bypass of CVE-2026-33937)
CVE-2026-106446 / GHSA-8r5x-fm3f-whwj
More information
Details
Summary
Handlebars.compile()andHandlebars.precompile()accept a pre-parsed AST as well as a template string. Handlebars 4.7.9 added validation for such ASTs. It only checks values onPathExpression,NumberLiteralandBooleanLiteralnodes, and the compiler still writes several other AST values into the generated JavaScript unchecked.If an application passes an untrusted object to
compile()orprecompile(), such as a parsed JSON request body, that object can carry arbitrary JavaScript. Withcompile(), the code runs on the server when the template renders. Withprecompile(), it ends up in the precompiled output and runs wherever that output is loaded.Applications that only ever pass template strings are not affected.
This bypasses the AST validation added in 4.7.9 for GHSA-2w6w-674q-4c4q, GHSA-xhpv-hc6g-r9c6 and GHSA-3mfm-83xf-c92r.
Details
The 4.7.9 validator runs in
parseWithoutProcessing()and walks the whole AST. It only checks values on nodes whosetypeisPathExpression,NumberLiteralorBooleanLiteral. It passes over plain objects without atype, and over strings and other non-object values. The compiler reads these values from other places and writes them into the generated code as-is:Program.blockParams.lengthcontainer.program(<index>, data, <length>)depthon a param that is not aPathExpression, e.g. aStringLiteraldepths[<depth>]stringParams: trueStringLiteral.valuethat is not a stringstringParams: truePathExpression.originalthat is not a stringstringParams: trueThe first row works with default options. A
ProgramwithblockParams: { "length": "<JS expression>" }passes validation because:blockParamsobject has notype, so none of the type-specific checks apply, andlengthbecause it is not an object.The compiler then reads
program.blockParams.lengthand writes the string directly into the template function. Thecontainer.program(...)call is part of the helper options object, which is built before the helper is looked up. The code therefore runs on render even if the block's helper does not exist.Proof of Concept
poc.json.missingHelperdoes not need to exist:{ "text": { "type": "Program", "loc": { "start": { "line": 1, "column": 0 } }, "body": [ { "type": "BlockStatement", "path": { "type": "PathExpression", "data": false, "depth": 0, "parts": ["missingHelper"], "original": "missingHelper", "loc": { "start": { "line": 1, "column": 0 } } }, "params": [], "program": { "type": "Program", "blockParams": { "length": "(()=>{throw new Error('Injected code ran as uid ' + process.getuid())})()" }, "body": [], "loc": { "start": { "line": 1, "column": 0 } } }, "openStrip": { "open": false, "close": false }, "inverseStrip": { "open": false, "close": false }, "closeStrip": { "open": false, "close": false }, "loc": { "start": { "line": 1, "column": 0 } } } ] } }The server responds with HTTP 500 and an error page showing the injected code ran inside the Node.js process:
Impact
An application is affected if untrusted input can reach
Handlebars.compile()orHandlebars.precompile()as an object instead of a string. A common way this happens is passing a field from a JSON request body straight tocompile().compile(): arbitrary JavaScript runs in the Node.js process with the application's privileges (remote code execution).precompile(): the injected code is written into the precompiled template and runs wherever that template is loaded, for example in users' browsers.Workarounds
Handlebars.compile(): ensure the argument is always astring, never a plain object or JSON-deserialized value.handlebars/runtime) on the server if templates are pre-compiled at build time;compile()will be unavailable.Credits
Variant
Program.blockParamsVariant
BooleanLiteral|NumberLiteral.valueVariant
PathExpression.depthSeverity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars: JavaScript Injection via Unsafe Inline Embedding of Precompiled Templates
CVE-2026-106444 / GHSA-xw65-4hp5-5hc7
More information
Details
Summary
Handlebars.precompile()generates JavaScript source that is commonly embedded in browser<script>elements. Before the fix, static template text containing</script>was emitted unchanged. HTML parsers recognize</script>even inside a JavaScript string literal, closing the surrounding script element and allowing following attacker-controlled markup to be parsed and executed.This affects applications that precompile attacker-controlled templates and embed the generated source directly in an HTML
<script>element. It does not affect ordinary server-side rendering or precompiled templates delivered as external JavaScript files.Details
Static text is serialized by
quotedString()inlib/handlebars/compiler/code-gen.js. The generated JavaScript is valid, but JavaScript quoting alone does not make it safe to embed in HTML. In HTML script data, the sequence</script>terminates the element regardless of JavaScript string context.On affected releases, this template:
could produce generated source containing:
When included inline in an HTML document, the first
</script>closes the script containing the precompiled template. The next<script>element is then parsed as HTML and executes.Proof of Concept
Affected versions print
true. Embeddingoutputdirectly in an inline<script>element allows the injected script tag to be parsed by the browser.Workarounds
Credits
Reported by Curly-Haired-Baboon Aka Laplas
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Handlebars: JavaScript Injection via Own Property Check Bypass
CVE-2026-106445 / GHSA-p8wg-vrv2-v86f
More information
Details
Summary
Handlebars can expose the
Functionconstructor despite its prototype-access deny list. When a template reachesFunction.prototype, its ownconstructorproperty is returned before the deny list is checked. An attacker who can render a controlled template withallowProtoMethodsByDefault: truecan inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.Description
lookupPropertytrusts own properties:Normally, prototype-derived values reach
resultIsAllowed, which blocks dangerous method names such asconstructor. However,constructoris itself an own property of prototype objects:As a result, once a template reaches
Function.prototype, looking upconstructorreturnsFunctionwithout 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:
Workarounds
Do not set
allowProtoMethodsByDefault: truewhen compiling untrusted templates with untrusted data.If you are using packages like hbs which set
allowProtoMethodsByDefault: trueby default, you have to update Handlebars to the latest version.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
handlebars-lang/handlebars.js (handlebars)
v4.7.10Compare Source
f37c599812c226<!--and<scriptin precompiled output -609d1b1GHSA-xw65-4hp5-5hc7ceec388GHSA-p8wg-vrv2-v86fc28ee7a7d501a5703fdccGHSA-8r5x-fm3f-whwj0fcf25cea8ed82d069c1c6714e07Compatibility notes:
{{#each}}iterates iterables such asMap,Setand generators lazily, likefor...of, instead ofcopying them into an array first. Values added to the iterable while the block renders are now visited too.
Commits
v4.7.9Compare Source
e0137c2eab1d14hashto be aRecord<string, any>-de4414d4512766e497a3568d8df5Commits
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.