CVE-2019-19919
Description
Versions of handlebars prior to 4.3.0 are vulnerable to Prototype Pollution leading to Remote Code Execution. Templates may alter an Object's __proto__ and __defineGetter__ properties, which may allow an attacker to execute arbitrary code through crafted payloads.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Handlebars.js before 4.3.0 allows prototype pollution via crafted templates, enabling remote code execution.
The vulnerability is a prototype pollution issue in Handlebars.js, a popular templating engine. Versions prior to 4.3.0 allow templates to manipulate an Object's __proto__ and __defineGetter__ properties, leading to potential remote code execution [1][2].
An attacker can exploit this by providing a crafted template that pollutes the Object prototype through special properties. This can be done by injecting malicious template expressions that modify __proto__ or __defineGetter__ [2]. The attack does not require authentication if the attacker can control the template input.
Successful exploitation allows the attacker to execute arbitrary code in the context of the application using Handlebars. This can lead to full compromise of the affected system, including data theft or further attacks [3].
The vulnerability was fixed in Handlebars.js version 4.3.0 [4]. Users should upgrade immediately. Vendor advisories have been published, and products like Tenable.sc have released patches to address this issue [3].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
handlebarsnpm | >= 4.0.0, < 4.3.0 | 4.3.0 |
bootstrap-wysihtml5-railsRubyGems | >= 0.3.3.5, <= 0.3.3.8 | — |
handlebarsnpm | < 3.0.8 | 3.0.8 |
Affected products
113- handlebars/handlebarsdescription
- osv-coords112 versionspkg:apk/chainguard/code-serverpkg:apk/chainguard/code-server-compatpkg:apk/chainguard/gitlab-rails-ce-18.1pkg:apk/chainguard/gitlab-rails-ce-18.2pkg:apk/chainguard/gitlab-rails-ce-18.3pkg:apk/chainguard/gitlab-rails-ce-18.4pkg:apk/chainguard/gitlab-rails-ce-18.5pkg:apk/chainguard/gitlab-rails-ce-18.6pkg:apk/chainguard/gitlab-rails-ce-18.7pkg:apk/chainguard/gitlab-rails-ce-assets-18.1pkg:apk/chainguard/gitlab-rails-ce-assets-18.11pkg:apk/chainguard/gitlab-rails-ce-assets-18.2pkg:apk/chainguard/gitlab-rails-ce-assets-18.3pkg:apk/chainguard/gitlab-rails-ce-assets-18.4pkg:apk/chainguard/gitlab-rails-ce-assets-18.5pkg:apk/chainguard/gitlab-rails-ce-assets-18.6pkg:apk/chainguard/gitlab-rails-ce-assets-18.7pkg:apk/chainguard/gitlab-rails-ce-assets-18.8pkg:apk/chainguard/gitlab-rails-ce-assets-18.9pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.1pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.11pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.2pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.3pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.4pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.5pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.6pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.7pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.8pkg:apk/chainguard/gitlab-rails-ce-assets-fips-18.9pkg:apk/chainguard/gitlab-rails-ce-doc-18.1pkg:apk/chainguard/gitlab-rails-ce-doc-18.2pkg:apk/chainguard/gitlab-rails-ce-doc-18.3pkg:apk/chainguard/gitlab-rails-ce-doc-18.4pkg:apk/chainguard/gitlab-rails-ce-doc-18.5pkg:apk/chainguard/gitlab-rails-ce-doc-18.6pkg:apk/chainguard/gitlab-rails-ce-doc-18.7pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.1pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.2pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.3pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.4pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.5pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.6pkg:apk/chainguard/gitlab-rails-ce-doc-fips-18.7pkg:apk/chainguard/gitlab-rails-ce-fips-18.1pkg:apk/chainguard/gitlab-rails-ce-fips-18.2pkg:apk/chainguard/gitlab-rails-ce-fips-18.3pkg:apk/chainguard/gitlab-rails-ce-fips-18.4pkg:apk/chainguard/gitlab-rails-ce-fips-18.5pkg:apk/chainguard/gitlab-rails-ce-fips-18.6pkg:apk/chainguard/gitlab-rails-ce-fips-18.7pkg:apk/chainguard/gitlab-rails-ee-17.0pkg:apk/chainguard/gitlab-rails-ee-17.1pkg:apk/chainguard/gitlab-rails-ee-17.10pkg:apk/chainguard/gitlab-rails-ee-17.11pkg:apk/chainguard/gitlab-rails-ee-17.2pkg:apk/chainguard/gitlab-rails-ee-17.3pkg:apk/chainguard/gitlab-rails-ee-17.4pkg:apk/chainguard/gitlab-rails-ee-17.6pkg:apk/chainguard/gitlab-rails-ee-17.7pkg:apk/chainguard/gitlab-rails-ee-17.8pkg:apk/chainguard/gitlab-rails-ee-17.9pkg:apk/chainguard/gitlab-rails-ee-assets-17.10pkg:apk/chainguard/gitlab-rails-ee-assets-17.11pkg:apk/chainguard/gitlab-rails-ee-assets-17.2pkg:apk/chainguard/gitlab-rails-ee-assets-17.3pkg:apk/chainguard/gitlab-rails-ee-assets-17.4pkg:apk/chainguard/gitlab-rails-ee-assets-17.6pkg:apk/chainguard/gitlab-rails-ee-assets-17.7pkg:apk/chainguard/gitlab-rails-ee-assets-17.8pkg:apk/chainguard/gitlab-rails-ee-assets-17.9pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.10pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.11pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.2pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.3pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.4pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.6pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.7pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.8pkg:apk/chainguard/gitlab-rails-ee-assets-fips-17.9pkg:apk/chainguard/gitlab-rails-ee-doc-17.10pkg:apk/chainguard/gitlab-rails-ee-doc-17.11pkg:apk/chainguard/gitlab-rails-ee-doc-17.2pkg:apk/chainguard/gitlab-rails-ee-doc-17.3pkg:apk/chainguard/gitlab-rails-ee-doc-17.4pkg:apk/chainguard/gitlab-rails-ee-doc-17.6pkg:apk/chainguard/gitlab-rails-ee-doc-17.7pkg:apk/chainguard/gitlab-rails-ee-doc-17.8pkg:apk/chainguard/gitlab-rails-ee-doc-17.9pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.10pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.11pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.2pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.3pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.4pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.6pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.7pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.8pkg:apk/chainguard/gitlab-rails-ee-doc-fips-17.9pkg:apk/chainguard/gitlab-rails-ee-fips-17.0pkg:apk/chainguard/gitlab-rails-ee-fips-17.1pkg:apk/chainguard/gitlab-rails-ee-fips-17.10pkg:apk/chainguard/gitlab-rails-ee-fips-17.11pkg:apk/chainguard/gitlab-rails-ee-fips-17.2pkg:apk/chainguard/gitlab-rails-ee-fips-17.3pkg:apk/chainguard/gitlab-rails-ee-fips-17.4pkg:apk/chainguard/gitlab-rails-ee-fips-17.6pkg:apk/chainguard/gitlab-rails-ee-fips-17.7pkg:apk/chainguard/gitlab-rails-ee-fips-17.8pkg:apk/chainguard/gitlab-rails-ee-fips-17.9pkg:apk/wolfi/code-serverpkg:apk/wolfi/code-server-compatpkg:gem/bootstrap-wysihtml5-railspkg:npm/handlebars
< 0+ 111 more
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: < 0
- (no CPE)range: >= 0.3.3.5, <= 0.3.3.8
- (no CPE)range: >= 4.0.0, < 4.3.0
Patches
390ad8d97ad29Update release notes
1 file changed · +24 −1
release-notes.md+24 −1 modified@@ -2,7 +2,30 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.7...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.8...master) + +## v3.0.8 - February 23rd, 2020 +Bugfixes: +- backport some (but not all) of the security fixes from 4.x - 156061e + +Compatibility notes: +- The properties `__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__` + have been added to the list of "dangerous properties". If a property + by that name is found and not an own-property of its parent, it will silently evaluate to undefined. + This is done in both the compiled template and the "lookup"-helper. This will prevent + Remote-Code-Execution exploits that have been published in npm advisories [1324](https://www.npmjs.com/advisories/1324) + and [1316](https://www.npmjs.com/advisories/1316). +- The check for dangerous properties has been changed from "propertyIsEnumerable" to "hasOwnProperty", as it is now done + in Handlebars 4.6.0 and later. + +Security issues resolved: +- [npm advisory 1324](https://www.npmjs.com/advisories/1324) +- [npm advisory 1316](https://www.npmjs.com/advisories/1316) +- [npm advisory 1325](https://www.npmjs.com/advisories/1325) +- [npm advisory 1164](https://www.npmjs.com/advisories/1164) + + +[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.7...v3.0.8) ## v3.0.7 - June 30th, 2019 Security fixes:
156061eb7707backport fixes from 4.x
4 files changed · +31 −8
lib/handlebars/base.js+1 −1 modified@@ -215,7 +215,7 @@ function registerDefaultHelpers(instance) { if (!obj) { return obj; } - if (field === 'constructor' && !obj.propertyIsEnumerable(field)) { + if (Utils.dangerousPropertyRegex.test(String(field)) && !Object.prototype.hasOwnProperty.call(obj, field)) { return undefined; } return obj[field];
lib/handlebars/compiler/javascript-compiler.js+12 −7 modified@@ -1,6 +1,6 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base'; import Exception from '../exception'; -import {isArray} from '../utils'; +import {isArray, dangerousPropertyRegex} from '../utils'; import CodeGen from './code-gen'; function Literal(value) { @@ -13,13 +13,18 @@ JavaScriptCompiler.prototype = { // PUBLIC API: You can override these methods in a subclass to provide // alternative compiled forms for name lookup and buffering semantics nameLookup: function(parent, name /* , type*/) { - if (name === 'constructor') { - return ['(', parent, '.propertyIsEnumerable(\'constructor\') ? ', parent, '.constructor : undefined', ')']; + if (dangerousPropertyRegex.test(name)) { + const isOwnProperty = [ this.aliasable('Object.prototype.hasOwnProperty'), '.call(', parent, ',', JSON.stringify(name), ')']; + return ['(', isOwnProperty, '?', _actualLookup(), ' : undefined)']; } - if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { - return [parent, '.', name]; - } else { - return [parent, "['", name, "']"]; + return _actualLookup(); + + function _actualLookup() { + if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + return [parent, '.', name]; + } else { + return [parent, '[', JSON.stringify(name), ']']; + } } }, depthedLookup: function(name) {
lib/handlebars/utils.js+2 −0 modified@@ -101,3 +101,5 @@ export function blockParams(params, ids) { export function appendContextPath(contextPath, id) { return (contextPath ? contextPath + '.' : '') + id; } + +export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/;
spec/security.js+16 −0 modified@@ -30,4 +30,20 @@ describe('security issues', function() { new TestClass(), 'xyz'); }); }); + + describe('GH-1595', function() { + it('properties, that are required to be enumerable', function() { + shouldCompileTo('{{constructor.name}}', {}, ''); + shouldCompileTo('{{__defineGetter__.name}}', {}, ''); + shouldCompileTo('{{__defineSetter__.name}}', {}, ''); + shouldCompileTo('{{__lookupGetter__.name}}', {}, ''); + shouldCompileTo('{{__proto__.__defineGetter__.name}}', {}, ''); + + shouldCompileTo('{{lookup this "constructor"}}', {}, ''); + shouldCompileTo('{{lookup this "__defineGetter__"}}', {}, ''); + shouldCompileTo('{{lookup this "__defineSetter__"}}', {}, ''); + shouldCompileTo('{{lookup this "__lookupGetter__"}}', {}, ''); + shouldCompileTo('{{lookup this "__proto__"}}', {}, ''); + }); + }); });
2078c727c627Disallow calling "helperMissing" and "blockHelperMissing" directly
6 files changed · +109 −25
lib/handlebars/compiler/javascript-compiler.js+23 −9 modified@@ -311,7 +311,7 @@ JavaScriptCompiler.prototype = { // replace it on the stack with the result of properly // invoking blockHelperMissing. blockValue: function(name) { - let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'), + let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), params = [this.contextName(0)]; this.setupHelperArgs(name, 0, params); @@ -329,7 +329,7 @@ JavaScriptCompiler.prototype = { // On stack, after, if lastHelper: value ambiguousBlockValue: function() { // We're being a bit cheeky and reusing the options value from the prior exec - let blockHelperMissing = this.aliasable('helpers.blockHelperMissing'), + let blockHelperMissing = this.aliasable('container.hooks.blockHelperMissing'), params = [this.contextName(0)]; this.setupHelperArgs('', 0, params, true); @@ -622,18 +622,32 @@ JavaScriptCompiler.prototype = { // If the helper is not found, `helperMissing` is called. invokeHelper: function(paramSize, name, isSimple) { let nonHelper = this.popStack(), - helper = this.setupHelper(paramSize, name), - simple = isSimple ? [helper.name, ' || '] : ''; + helper = this.setupHelper(paramSize, name); - let lookup = ['('].concat(simple, nonHelper); + let possibleFunctionCalls = []; + + if (isSimple) { // direct call to helper + possibleFunctionCalls.push(helper.name); + } + // call a function from the input object + possibleFunctionCalls.push(nonHelper); if (!this.options.strict) { - lookup.push(' || ', this.aliasable('helpers.helperMissing')); + possibleFunctionCalls.push(this.aliasable('container.hooks.helperMissing')); } - lookup.push(')'); - this.push(this.source.functionCall(lookup, 'call', helper.callParams)); + let functionLookupCode = ['(', this.itemsSeparatedBy(possibleFunctionCalls, '||'), ')']; + let functionCall = this.source.functionCall(functionLookupCode, 'call', helper.callParams); + this.push(functionCall); }, + itemsSeparatedBy: function(items, separator) { + let result = []; + result.push(items[0]); + for (let i = 1; i < items.length; i++) { + result.push(separator, items[i]); + } + return result; + }, // [invokeKnownHelper] // // On stack, before: hash, inverse, program, params..., ... @@ -673,7 +687,7 @@ JavaScriptCompiler.prototype = { lookup[0] = '(helper = '; lookup.push( ' != null ? helper : ', - this.aliasable('helpers.helperMissing') + this.aliasable('container.hooks.helperMissing') ); }
lib/handlebars/helpers.js+9 −0 modified@@ -15,3 +15,12 @@ export function registerDefaultHelpers(instance) { registerLookup(instance); registerWith(instance); } + +export function moveHelperToHooks(instance, helperName, keepHelper) { + if (instance.helpers[helperName]) { + instance.hooks[helperName] = instance.helpers[helperName]; + if (!keepHelper) { + delete instance.helpers[helperName]; + } + } +}
lib/handlebars/runtime.js+19 −16 modified@@ -1,6 +1,7 @@ import * as Utils from './utils'; import Exception from './exception'; -import { COMPILER_REVISION, REVISION_CHANGES, createFrame } from './base'; +import {COMPILER_REVISION, createFrame, REVISION_CHANGES} from './base'; +import {moveHelperToHooks} from './helpers'; export function checkRevision(compilerInfo) { const compilerRevision = compilerInfo && compilerInfo[0] || 1, @@ -21,6 +22,7 @@ export function checkRevision(compilerInfo) { } export function template(templateSpec, env) { + /* istanbul ignore next */ if (!env) { throw new Exception('No environment passed to template'); @@ -42,13 +44,15 @@ export function template(templateSpec, env) { options.ids[0] = true; } } - partial = env.VM.resolvePartial.call(this, partial, context, options); - let result = env.VM.invokePartial.call(this, partial, context, options); + + let optionsWithHooks = Utils.extend({}, options, {hooks: this.hooks}); + + let result = env.VM.invokePartial.call(this, partial, context, optionsWithHooks); if (result == null && env.compile) { options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); - result = options.partials[options.name](context, options); + result = options.partials[options.name](context, optionsWithHooks); } if (result != null) { if (options.indent) { @@ -115,15 +119,6 @@ export function template(templateSpec, env) { } return value; }, - merge: function(param, common) { - let obj = param || common; - - if (param && common && (param !== common)) { - obj = Utils.extend({}, common, param); - } - - return obj; - }, // An empty object to use as replacement for null-contexts nullContext: Object.seal({}), @@ -158,19 +153,27 @@ export function template(templateSpec, env) { ret._setup = function(options) { if (!options.partial) { - container.helpers = container.merge(options.helpers, env.helpers); + container.helpers = Utils.extend({}, env.helpers, options.helpers); if (templateSpec.usePartial) { - container.partials = container.merge(options.partials, env.partials); + container.partials = Utils.extend({}, env.partials, options.partials); } if (templateSpec.usePartial || templateSpec.useDecorators) { - container.decorators = container.merge(options.decorators, env.decorators); + container.decorators = Utils.extend({}, env.decorators, options.decorators); } + + container.hooks = {}; + let keepHelper = options.allowCallsToHelperMissing; + moveHelperToHooks(container, 'helperMissing', keepHelper); + moveHelperToHooks(container, 'blockHelperMissing', keepHelper); + } else { container.helpers = options.helpers; container.partials = options.partials; container.decorators = options.decorators; + container.hooks = options.hooks; } + }; ret._child = function(i, data, blockParams, depths) {
lib/handlebars/utils.js+1 −0 modified@@ -1,3 +1,4 @@ + const escape = { '&': '&', '<': '<',
spec/security.js+56 −0 modified@@ -32,4 +32,60 @@ describe('security issues', function() { }); }); + + describe('GH-xxxx: Prevent explicit call of helperMissing-helpers', function() { + if (!Handlebars.compile) { + return; + } + + describe('without the option "allowExplicitCallOfHelperMissing"', function() { + it('should throw an exception when calling "{{helperMissing}}" ', function() { + shouldThrow(function() { + var template = Handlebars.compile('{{helperMissing}}'); + template({}); + }, Error); + }); + it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { + shouldThrow(function() { + var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}'); + template({}); + }, Error); + }); + it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { + var functionCalls = []; + shouldThrow(function() { + var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); + template({ fn: function() { functionCalls.push('called'); }}); + }, Error); + equals(functionCalls.length, 0); + }); + it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { + shouldThrow(function() { + var template = Handlebars.compile('{{#blockHelperMissing .}}{{/blockHelperMissing}}'); + template({ fn: function() { return 'functionInData';}}); + }, Error); + }); + }); + + describe('with the option "allowCallsToHelperMissing" set to true', function() { + it('should not throw an exception when calling "{{helperMissing}}" ', function() { + var template = Handlebars.compile('{{helperMissing}}'); + template({}, {allowCallsToHelperMissing: true}); + }); + it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() { + var template = Handlebars.compile('{{#helperMissing}}{{/helperMissing}}'); + template({}, {allowCallsToHelperMissing: true}); + }); + it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() { + var functionCalls = []; + var template = Handlebars.compile('{{blockHelperMissing "abc" .}}'); + template({ fn: function() { functionCalls.push('called'); }}, {allowCallsToHelperMissing: true}); + equals(functionCalls.length, 1); + }); + it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() { + var template = Handlebars.compile('{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'); + template({}, {allowCallsToHelperMissing: true}); + }); + }); + }); });
types/index.d.ts+1 −0 modified@@ -29,6 +29,7 @@ declare namespace Handlebars { decorators?: { [name: string]: Function }; data?: any; blockParams?: any[]; + allowCallsToHelperMissing: boolean; } export interface HelperOptions {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- github.com/advisories/GHSA-w457-6q6x-cgp9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-19919ghsaADVISORY
- cve.mitre.org/cgi-bin/cvename.cgighsaWEB
- github.com/Nerian/bootstrap-wysihtml5-rails/blob/master/vendor/assets/javascripts/bootstrap-wysihtml5/handlebars.runtime.min.jsghsaWEB
- github.com/Nerian/bootstrap-wysihtml5-rails/tree/master/vendor/assets/javascripts/bootstrap-wysihtml5ghsaWEB
- github.com/handlebars-lang/handlebars.js/commit/156061eb7707575293613d7fdf90e2bdaac029eeghsaWEB
- github.com/handlebars-lang/handlebars.js/commit/90ad8d97ad2933852fb83fcc054699dc99e094dbghsaWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/bootstrap-wysihtml5-rails/CVE-2019-19919.ymlghsaWEB
- github.com/wycats/handlebars.js/commit/2078c727c627f25d4a149962f05c1e069beb18bcghsaWEB
- github.com/wycats/handlebars.js/issues/1558ghsaWEB
- www.npmjs.com/advisories/1164mitrex_refsource_MISC
- www.tenable.com/security/tns-2021-14ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.