From c22099e5cc72b2b5e7c2efdb61314d680bb92d07 Mon Sep 17 00:00:00 2001 From: Matt Olson Date: Fri, 28 Jun 2019 16:05:05 -0700 Subject: [PATCH 1/2] STRF-6929: Fix RCE in Handlebars --- components/bower.json | 2 +- components/handlebars.js.nuspec | 2 +- components/package.json | 2 +- lib/handlebars/base.js | 8 ++++- .../compiler/javascript-compiler.js | 3 ++ package.json | 2 +- release-notes.md | 27 ++++++++++++++- spec/security.js | 33 +++++++++++++++++++ 8 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 spec/security.js diff --git a/components/bower.json b/components/bower.json index f1f2d52ff..458597bd7 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "3.0.6", + "version": "3.1.0", "main": "handlebars.js", "dependencies": {} } diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index 108c138b3..863b9c9d5 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ handlebars.js - 3.0.6 + 3.1.0 handlebars.js Authors https://github.com/wycats/handlebars.js/blob/master/LICENSE https://github.com/wycats/handlebars.js/ diff --git a/components/package.json b/components/package.json index 294cd9255..b3d659516 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "3.0.6", + "version": "3.1.0", "license": "MIT", "jspm": { "main": "handlebars", diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index cfe1e917c..5342f294f 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -212,7 +212,13 @@ function registerDefaultHelpers(instance) { }); instance.registerHelper('lookup', function(obj, field) { - return obj && obj[field]; + if (!obj) { + return obj; + } + if (field === 'constructor' && !obj.propertyIsEnumerable(field)) { + return undefined; + } + return obj[field]; }); } diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 883066165..f070a39f9 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -13,6 +13,9 @@ JavaScriptCompiler.prototype = { // PUBLIC API: You can override these methods in a subclass to provide // alternative compiled forms for name lookup and buffering semantics nameLookup: function(parent, name /* , type*/) { + if (name === 'constructor') { + return ['(', parent, '.propertyIsEnumerable(\'constructor\') ? ', parent, '.constructor : undefined', ')']; + } if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { return [parent, '.', name]; } else { diff --git a/package.json b/package.json index 1282ab902..c1a3e0381 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "3.0.6", + "version": "3.1.0", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [ diff --git a/release-notes.md b/release-notes.md index 4c6931515..dd4c6db60 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,32 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v3.0.6...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v3.1.0...master) + +## v3.1.0 - June 28, 2019 + +Backport of security fixes from 4.1.0 and 4.1.2: + +- disallow access to the constructor in templates to prevent RCE +- prevent RCE through the "lookup"-helper + +Compatibility notes: + +Access to class constructors (i.e. `({}).constructor`) is now prohibited to prevent +Remote Code Execution. This means that following construct will no work anymore: + +``` +class SomeClass { +} +SomeClass.staticProperty = 'static' +var template = Handlebars.compile('{{constructor.staticProperty}}'); +document.getElementById('output').innerHTML = template(new SomeClass()); +// expected: 'static', but now this is empty. +``` + +Similarly, access to the constructor of a class through {{lookup obj "constructor" }} is now prohibited. + +This kind of access is not the intended use of Handlebars and leads to the vulnerability described in #1495. We will **not** increase the major version, because such use is not intended or documented, and because of the potential impact of the issue (we fear that most people won't use a new major version and the issue may not be resolved on many systems). ## v3.0.6 - January 2nd, 2019 Chore: diff --git a/spec/security.js b/spec/security.js new file mode 100644 index 000000000..5af5334eb --- /dev/null +++ b/spec/security.js @@ -0,0 +1,33 @@ +describe('security issues', function() { + describe('GH-1495: Prevent Remote Code Execution via constructor', function() { + it('should not allow constructors to be accessed', function() { + shouldCompileTo('{{constructor.name}}', {}, ''); + shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {}, ''); + }); + + it('should allow the "constructor" property to be accessed if it is enumerable', function() { + shouldCompileTo('{{constructor.name}}', {'constructor': { + 'name': 'here we go' + }}, 'here we go'); + shouldCompileTo('{{lookup (lookup this "constructor") "name"}}', {'constructor': { + 'name': 'here we go' + }}, 'here we go'); + }); + + it('should allow prototype properties that are not constructors', function() { + function TestClass() { + } + + Object.defineProperty(TestClass.prototype, 'abc', { + get: function() { + return 'xyz'; + } + }); + + shouldCompileTo('{{#with this}}{{this.abc}}{{/with}}', + new TestClass(), 'xyz'); + shouldCompileTo('{{#with this}}{{lookup this "abc"}}{{/with}}', + new TestClass(), 'xyz'); + }); + }); +}); From 322c2b33b882b654864716300dd254dcd0bad35e Mon Sep 17 00:00:00 2001 From: Matt Olson Date: Sat, 29 Jun 2019 09:50:38 -0700 Subject: [PATCH 2/2] Fix git tag retrieval for non-master branch --- tasks/util/git.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/util/git.js b/tasks/util/git.js index a6c9ec1dc..abb7723e8 100644 --- a/tasks/util/git.js +++ b/tasks/util/git.js @@ -86,7 +86,7 @@ module.exports = { }); }, tagName: function(callback) { - childProcess.exec('git describe --tags', {}, function(err, stdout) { + childProcess.exec('git describe --tags --always', {}, function(err, stdout) { if (err) { throw new Error('git.tagName: ' + err.message); }