[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#947758: marked as done (buster-pu: package node-handlebars/3:4.1.0-1+deb10u1)



Your message dated Sat, 01 Aug 2020 12:51:28 +0100
with message-id <43535efb498a168cf81452ca0c326f004f46adc6.camel@adam-barratt.org.uk>
and subject line Closing bugs for fixes included in 10.5 point release
has caused the Debian Bug report #947758,
regarding buster-pu: package node-handlebars/3:4.1.0-1+deb10u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
947758: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=947758
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: buster
User: release.debian.org@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
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@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@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@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

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 10.5

Hi,

Each of these bugs relates to an update that was included in today's
stable point release.

Regards,

Adam

--- End Message ---

Reply to: