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 <y...@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 <y...@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 <n...@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 <y...@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