Bug#947758: buster-pu: package node-handlebars/3:4.1.0-1+deb10u1
Le 04/05/2020 à 11:54, Adam D. Barratt a écrit :
> On Mon, 2020-05-04 at 11:36 +0200, Xavier wrote:
>> Le 02/05/2020 à 11:58, Adam D. Barratt a écrit :
>>> On Sat, 2020-04-25 at 21:30 +0200, Paul Gevers wrote:
>>>> Hi Xavier,
>>>>
>>>> On Sat, 8 Feb 2020 08:23:25 +0100 Xavier <yadd@debian.org> wrote:
>>>>> Le 07/02/2020 à 20:16, Adam D. Barratt a écrit :
>>>>>> On Sat, 2020-01-25 at 20:40 +0000, Adam D. Barratt wrote:
>>>>>> This apparently causes regressions in the autopkgtests of
>>>>>> node-
>>>>>> markdown-it-html5-embed, which you also most recently
>>>>>> uploaded -
>>>>>> see
>>>>>> https://ci.debian.net/user/britney/jobs?package=node-markdown-it-html5-embed&suite[]=stable&arch[]=amd64
>>>>>>
>>>>>> Is this enough of an issue to not include the node-handlebars
>>>>>> update?
>>>>>>
>>>>>> Regards,
>>>>>>
>>>>>> Adam
>>>>>
>>>>> Hi,
>>>>>
>>>>> then please defer node-handlebars update until I understand
>>>>> what
>>>>> happens.
>>>>
>>>> Did you figure this out in the mean time? The next point release
>>>> is
>>>> going to happen on 9 May 2020, so it would be good to know if the
>>>> package can be included.
>>>
>>> Ping?
>>>
>>> Regards,
>>>
>>> Adam
>>
>> Hi,
>>
>> Sorry for the delay.
>>
>> handlebar patch is based on some other commits, its test succeeds but
>> renders it unusable as shown by node-markdown-it-html5-embed
>> regression.
>> I've to pick some other commits...
>
> Thanks for getting back to us.
>
> The window for getting fixes into 10.4 closed yesterday, so I guess
> we'll be excluding node-handlebars again?
>
> Regards,
>
> Adam
Finally I found a way to fix CVE and keep autopkgtest OK
(node-markdown-it-html5-embed). Here is a debdiff for a future point release
Cheers,
Xavier
diff --git a/debian/changelog b/debian/changelog
index b985661..64df8db 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, 04 May 2020 14:21:11 +0200
+
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..d34e77a
--- /dev/null
+++ b/debian/patches/CVE-2019-19919.patch
@@ -0,0 +1,228 @@
+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/helpers.js
++++ b/lib/handlebars/helpers.js
+@@ -15,3 +15,12 @@
+ 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];
++ }
++ }
++}
+--- 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
Reply to: