[Pkg-javascript-devel] Bug#947758: buster-pu: package node-handlebars/3:4.1.0-1+deb10u1
Xavier Guimard
yadd at debian.org
Mon Dec 30 06:51:53 GMT 2019
Package: release.debian.org
Severity: normal
Tags: buster
User: release.debian.org at packages.debian.org
Usertags: pu
Hi,
node-handlebars is vulnearable to prototype pollution (CVE-2019-19919).
This patch is exactly the one of upstream.
Cheers,
Xavier
-------------- next part --------------
diff --git a/debian/changelog b/debian/changelog
index b985661..95811b9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+node-handlebars (3:4.1.0-1+deb10u1) buster; urgency=medium
+
+ * Team upload
+ * Disallow calling "helperMissing" and "blockHelperMissing" directly
+ (Closes: CVE-2019-19919)
+
+ -- Xavier Guimard <yadd at debian.org> Mon, 30 Dec 2019 07:46:39 +0100
+
node-handlebars (3:4.1.0-1) unstable; urgency=medium
* New upstream version 4.1.0 (Closes: #923042)
diff --git a/debian/patches/CVE-2019-19919.patch b/debian/patches/CVE-2019-19919.patch
new file mode 100644
index 0000000..f63f106
--- /dev/null
+++ b/debian/patches/CVE-2019-19919.patch
@@ -0,0 +1,213 @@
+Description: Disallow calling "helperMissing" and "blockHelperMissing" directly
+ Fix for CVE-2019-19919
+Author: Nils Knappmeier <npm at knappi.org>
+Origin: upstream, https://github.com/wycats/handlebars.js/commit/2078c72
+Bug: https://github.com/wycats/handlebars.js/issues/1558
+Forwarded: not-needed
+Reviewed-By: Xavier Guimard <yadd at debian.org>
+Last-Update: 2019-12-30
+
+--- a/lib/handlebars/compiler/javascript-compiler.js
++++ b/lib/handlebars/compiler/javascript-compiler.js
+@@ -311,7 +311,7 @@
+ // 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 @@
+ // 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,31 @@
+ // 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 +686,7 @@
+ lookup[0] = '(helper = ';
+ lookup.push(
+ ' != null ? helper : ',
+- this.aliasable('helpers.helperMissing')
++ this.aliasable('container.hooks.helperMissing')
+ );
+ }
+
+--- a/lib/handlebars/runtime.js
++++ b/lib/handlebars/runtime.js
+@@ -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,
+@@ -44,11 +45,14 @@
+ }
+
+ 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 @@
+ }
+ 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,18 +153,25 @@
+
+ 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;
+ }
+ };
+
+--- a/spec/security.js
++++ b/spec/security.js
+@@ -20,4 +20,60 @@
+ new TestClass(), 'xyz');
+ });
+ });
++
++ 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});
++ });
++ });
++ });
+ });
diff --git a/debian/patches/series b/debian/patches/series
index c78fac2..7cf0804 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,4 @@
port-to-babel6-webpack3.patch
skip-some-modules.patch
use-system-jison.patch
+CVE-2019-19919.patch
More information about the Pkg-javascript-devel
mailing list