[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