From edc6220d51139b32c28e51641fadad59a543ae57 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 30 Jan 2019 22:21:44 +0100 Subject: [PATCH 01/12] fix: disallow access to the constructor in templates to prevent RCE This commit fixes a Remote Code Execution (RCE) reported by npm-security. Access to non-enumerable "constructor"-properties is now prohibited by the compiled template-code, because this the first step on the way to creating and execution arbitrary JavaScript code. The vulnerability affects systems where an attacker is allowed to inject templates into the Handlebars setup. Further details of the attack may be disclosed by npm-security. Closes #1267 Closes #1495 --- .../compiler/javascript-compiler.js | 3 +++ spec/security.js | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 spec/security.js diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 471144dd..ff98ad9e 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/spec/security.js b/spec/security.js new file mode 100644 index 00000000..45b96c34 --- /dev/null +++ b/spec/security.js @@ -0,0 +1,23 @@ +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}}', {}, ''); + }); + + 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'); + }); + + it('should allow prototype properties that are not constructors', function() { + class TestClass { + get abc() { + return 'xyz'; + } + } + shouldCompileTo('{{#with this as |obj|}}{{obj.abc}}{{/with}}', + new TestClass(), 'xyz'); + }); + }); +}); From 2db0d123c8501a7cf67b8252523ac3000c9c028f Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 08:42:04 +0100 Subject: [PATCH 02/12] chore: add .idea and yarn-error.log to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3c6d099f..42b64dde 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ node_modules *.sublime-workspace npm-debug.log sauce_connect.log* +.idea +yarn-error.log From 05e6293bb37979d39d020d233c42756c8132ad0e Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 09:53:09 +0100 Subject: [PATCH 03/12] chore: bump version of grunt-saucelabs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f18bcff..379eb273 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "grunt-contrib-uglify": "^1", "grunt-contrib-watch": "^1.1.0", "grunt-eslint": "^20.1.0", - "grunt-saucelabs": "8.x", + "grunt-saucelabs": "9.x", "grunt-webpack": "^1.0.8", "istanbul": "^0.3.0", "jison": "~0.3.0", From ee3022228b40ae595e1574923362d8a6db0ec2d7 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 10:04:17 +0100 Subject: [PATCH 04/12] chore: disable sauce-labs Related to #1497 --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index ce85e9a4..7542a7ff 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -228,7 +228,7 @@ module.exports = function(grunt) { grunt.task.loadTasks('tasks'); grunt.registerTask('bench', ['metrics']); - grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []); + grunt.registerTask('sauce', [] /* process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : [] */); grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'sauce', 'metrics', 'publish:latest'] : ['default']); From 56fc6768d1231e8e4d7cd37ba0ff792a1db82f98 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 10:14:44 +0100 Subject: [PATCH 05/12] test: run appveyor tests in Node 10 --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b67fb4ca..563aaf93 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,7 @@ # Test against these versions of Node.js environment: matrix: - - nodejs_version: "4" - - nodejs_version: "5" + - nodejs_version: "10" platform: - x64 From 42841c41a49bf9dc0c9c2eebefd54f35b38d9544 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 30 Jan 2019 22:21:44 +0100 Subject: [PATCH 06/12] fix: disallow access to the constructor in templates to prevent RCE This commit fixes a Remote Code Execution (RCE) reported by npm-security. Access to non-enumerable "constructor"-properties is now prohibited by the compiled template-code, because this the first step on the way to creating and execution arbitrary JavaScript code. The vulnerability affects systems where an attacker is allowed to inject templates into the Handlebars setup. Further details of the attack may be disclosed by npm-security. Closes #1267 Closes #1495 --- .../compiler/javascript-compiler.js | 3 +++ spec/security.js | 23 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 spec/security.js diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index 471144dd..ff98ad9e 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/spec/security.js b/spec/security.js new file mode 100644 index 00000000..45b96c34 --- /dev/null +++ b/spec/security.js @@ -0,0 +1,23 @@ +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}}', {}, ''); + }); + + 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'); + }); + + it('should allow prototype properties that are not constructors', function() { + class TestClass { + get abc() { + return 'xyz'; + } + } + shouldCompileTo('{{#with this as |obj|}}{{obj.abc}}{{/with}}', + new TestClass(), 'xyz'); + }); + }); +}); From c6a8fc1c045b6b7d6fdb94a32b991b2803bff205 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 08:42:04 +0100 Subject: [PATCH 07/12] chore: add .idea and yarn-error.log to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3c6d099f..42b64dde 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ node_modules *.sublime-workspace npm-debug.log sauce_connect.log* +.idea +yarn-error.log From dbc50ac7050f030743bc9a1186c1eea521fe1fff Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 09:53:09 +0100 Subject: [PATCH 08/12] chore: bump version of grunt-saucelabs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f18bcff..379eb273 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "grunt-contrib-uglify": "^1", "grunt-contrib-watch": "^1.1.0", "grunt-eslint": "^20.1.0", - "grunt-saucelabs": "8.x", + "grunt-saucelabs": "9.x", "grunt-webpack": "^1.0.8", "istanbul": "^0.3.0", "jison": "~0.3.0", From f1c8b2e2a2837e77f188e4e6cbadaae0749d5628 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 10:04:17 +0100 Subject: [PATCH 09/12] chore: disable sauce-labs Related to #1497 --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index ce85e9a4..7542a7ff 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -228,7 +228,7 @@ module.exports = function(grunt) { grunt.task.loadTasks('tasks'); grunt.registerTask('bench', ['metrics']); - grunt.registerTask('sauce', process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : []); + grunt.registerTask('sauce', [] /* process.env.SAUCE_USERNAME ? ['tests', 'connect', 'saucelabs-mocha'] : [] */); grunt.registerTask('travis', process.env.PUBLISH ? ['default', 'sauce', 'metrics', 'publish:latest'] : ['default']); From b02e9a25eea6a8829e5b794f5506d7806d48c73b Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 10:14:44 +0100 Subject: [PATCH 10/12] test: run appveyor tests in Node 10 --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b67fb4ca..563aaf93 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,7 @@ # Test against these versions of Node.js environment: matrix: - - nodejs_version: "4" - - nodejs_version: "5" + - nodejs_version: "10" platform: - x64 From 7bd34fb4662c69a86e654311ff317f5710c9c11e Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 10:46:01 +0100 Subject: [PATCH 11/12] Update release notes --- release-notes.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 8af10379..5b54c5e7 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,44 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.12...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v4.1.0...master) + +## v4.1.0 - February 7th, 2019 +New Features + +- import TypeScript typings - 27ac1ee + +Security fixes: + +- disallow access to the constructor in templates to prevent RCE - 42841c4, #1495 + +Housekeeping + +- chore: fix components/handlebars package.json and auto-update on release - bacd473 +- chore: Use node 10 to build handlebars - 78dd89c +- chore/doc: Add more release docs - 6b87c21 + +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. +``` + +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). + + + +[Commits](https://github.com/wycats/handlebars.js/compare/v4.0.12...v4.1.0) ## v4.0.12 - September 4th, 2018 New features: From 7caca944b1ae64b5bc11cba67d21e4b51ba6196a Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 7 Feb 2019 10:46:32 +0100 Subject: [PATCH 12/12] v4.1.0 --- components/bower.json | 2 +- components/handlebars.js.nuspec | 2 +- components/package.json | 2 +- lib/handlebars/base.js | 2 +- package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/bower.json b/components/bower.json index 2bf984f2..af87b72a 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.0.12", + "version": "4.1.0", "main": "handlebars.js", "license": "MIT", "dependencies": {} diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index bb463ce5..a4740212 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ handlebars.js - 4.0.12 + 4.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 a81e45d9..7013be4a 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.0.12", + "version": "4.1.0", "license": "MIT", "jspm": { "main": "handlebars", diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 127be4b7..22307b9d 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -4,7 +4,7 @@ import {registerDefaultHelpers} from './helpers'; import {registerDefaultDecorators} from './decorators'; import logger from './logger'; -export const VERSION = '4.0.12'; +export const VERSION = '4.1.0'; export const COMPILER_REVISION = 7; export const REVISION_CHANGES = { diff --git a/package.json b/package.json index 379eb273..de4efbf8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "4.0.12", + "version": "4.1.0", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [