From c2ac79c9705443d5fcabf87cda14392058b9ac56 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 13 Nov 2019 21:38:32 +0100 Subject: [PATCH 1/4] test: add fluent API for testing Handlebars use "expectTemplate(template)....toCompileTo(output)" --- spec/.eslintrc | 1 + spec/env/common.js | 86 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/spec/.eslintrc b/spec/.eslintrc index 421bd00d2..34319f8c1 100644 --- a/spec/.eslintrc +++ b/spec/.eslintrc @@ -13,6 +13,7 @@ "shouldCompileTo": true, "shouldCompileToWithPartials": true, "shouldThrow": true, + "expectTemplate": true, "compileWithPartials": true, "console": true, "require": true, diff --git a/spec/env/common.js b/spec/env/common.js index 14d302aa9..9523eaeb2 100644 --- a/spec/env/common.js +++ b/spec/env/common.js @@ -1,4 +1,6 @@ -var global = (function() { return this; }()); +var global = (function() { + return this; +}()); var AssertError; if (Error.captureStackTrace) { @@ -16,10 +18,16 @@ if (Error.captureStackTrace) { AssertError = Error; } +/** + * @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead + */ global.shouldCompileTo = function(string, hashOrArray, expected, message) { shouldCompileToWithPartials(string, hashOrArray, false, expected, message); }; +/** + * @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead + */ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string, hashOrArray, partials, expected, message) { var result = compileWithPartials(string, hashOrArray, partials); if (result !== expected) { @@ -27,6 +35,9 @@ global.shouldCompileToWithPartials = function shouldCompileToWithPartials(string } }; +/** + * @deprecated Use "expectTemplate(template)...toCompileTo(output)" instead + */ global.compileWithPartials = function(string, hashOrArray, partials) { var template, ary, @@ -36,8 +47,8 @@ global.compileWithPartials = function(string, hashOrArray, partials) { delete hashOrArray.hash; } else if (Object.prototype.toString.call(hashOrArray) === '[object Array]') { ary = []; - ary.push(hashOrArray[0]); - ary.push({ helpers: hashOrArray[1], partials: hashOrArray[2] }); + ary.push(hashOrArray[0]); // input + ary.push({helpers: hashOrArray[1], partials: hashOrArray[2]}); options = typeof hashOrArray[3] === 'object' ? hashOrArray[3] : {compat: hashOrArray[3]}; if (hashOrArray[4] != null) { options.data = !!hashOrArray[4]; @@ -75,3 +86,72 @@ global.shouldThrow = function(callback, type, msg) { throw new AssertError('It failed to throw', shouldThrow); } }; + + +global.expectTemplate = function(templateAsString) { + return new HandlebarsTestBench(templateAsString); +}; + + +function HandlebarsTestBench(templateAsString) { + this.templateAsString = templateAsString; + this.helpers = {}; + this.partials = {}; + this.input = {}; + this.message = 'Template' + templateAsString + ' does not evaluate to expected output'; + this.compileOptions = {}; + this.runtimeOptions = {}; +} + +HandlebarsTestBench.prototype.withInput = function(input) { + this.input = input; + return this; +}; + +HandlebarsTestBench.prototype.withHelper = function(name, helperFunction) { + this.helpers[name] = helperFunction; + return this; +}; + +HandlebarsTestBench.prototype.withPartial = function(name, partialAsString) { + this.partials[name] = partialAsString; + return this; +}; + +HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) { + this.compileOptions = compileOptions; + return this; +}; + +HandlebarsTestBench.prototype.withRuntimeOptions = function(runtimeOptions) { + this.runtimeOptions = runtimeOptions; + return this; +}; + +HandlebarsTestBench.prototype.withMessage = function(message) { + this.message = message; + return this; +}; + +HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) { + var self = this; + var compile = Object.keys(this.partials).length > 0 + ? CompilerContext.compileWithPartial + : CompilerContext.compile; + + var combinedRuntimeOptions = {}; + Object.keys(this.runtimeOptions).forEach(function(key) { + combinedRuntimeOptions[key] = self.runtimeOptions[key]; + }); + combinedRuntimeOptions.helpers = this.helpers; + combinedRuntimeOptions.partials = this.partials; + + var template = compile(this.templateAsString, this.compileOptions); + var output = template(this.input, combinedRuntimeOptions); + + if (output !== expectedOutputAsString) { + // Error message formatted so that IntelliJ-Idea shows "diff"-button + // https://stackoverflow.com/a/10945655/4251384 + throw new AssertError(this.message + '\nexpected:' + expectedOutputAsString + 'but was:' + output); + } +}; From d54137810a49939fd2ad01a91a34e182ece4528e Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 13 Nov 2019 21:41:36 +0100 Subject: [PATCH 2/4] fix: use String(field) in lookup when checking for "constructor" closes #1603 --- lib/handlebars/helpers/lookup.js | 2 +- spec/security.js | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/handlebars/helpers/lookup.js b/lib/handlebars/helpers/lookup.js index dcd585178..0654cc393 100644 --- a/lib/handlebars/helpers/lookup.js +++ b/lib/handlebars/helpers/lookup.js @@ -3,7 +3,7 @@ export default function(instance) { if (!obj) { return obj; } - if (field === 'constructor' && !obj.propertyIsEnumerable(field)) { + if (String(field) === 'constructor' && !obj.propertyIsEnumerable(field)) { return undefined; } return obj[field]; diff --git a/spec/security.js b/spec/security.js index c50bda6d0..80b8e23c2 100644 --- a/spec/security.js +++ b/spec/security.js @@ -1,11 +1,26 @@ 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"}}', {}, ''); + expectTemplate('{{lookup (lookup this "constructor") "name"}}') + .withInput({}) + .toCompileTo(''); + + expectTemplate('{{constructor.name}}') + .withInput({}) + .toCompileTo(''); }); - it('should allow the "constructor" property to be accessed if it is enumerable', function() { + it('GH-1603: should not allow constructors to be accessed (lookup via toString)', function() { + expectTemplate('{{lookup (lookup this (list "constructor")) "name"}}') + .withInput({}) + .withHelper('list', function(element) { + return [element]; + }) + .toCompileTo(''); + }); + + + 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'); @@ -14,6 +29,13 @@ describe('security issues', function() { }}, 'here we go'); }); + it('should allow the "constructor" property to be accessed if it is enumerable', function() { + 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() { } From 6914090086cf9e108c13637395a3f92b8887a585 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 13 Nov 2019 22:06:51 +0100 Subject: [PATCH 3/4] Update release notes --- release-notes.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/release-notes.md b/release-notes.md index 11b1d0146..3eefabe89 100644 --- a/release-notes.md +++ b/release-notes.md @@ -2,7 +2,18 @@ ## Development -[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.1...master) +[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.2...master) + +## v4.5.2 - November 13th, 2019 +# Bugfixes + +- fix: use String(field) in lookup when checking for "constructor" - d541378 +- test: add fluent API for testing Handlebars - c2ac79c + +Compatibility notes: +- no incompatibility are to be expected + +[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.1...v4.5.2) ## v4.5.1 - October 29th, 2019 Bugfixs From 8de121d21c88a6ab877e3a0eec9daaac483f3bf0 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Wed, 13 Nov 2019 22:07:27 +0100 Subject: [PATCH 4/4] v4.5.2 --- 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 fada3225e..53453c96d 100644 --- a/components/bower.json +++ b/components/bower.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.5.1", + "version": "4.5.2", "main": "handlebars.js", "license": "MIT", "dependencies": {} diff --git a/components/handlebars.js.nuspec b/components/handlebars.js.nuspec index d7a2dffec..a4ec49b01 100644 --- a/components/handlebars.js.nuspec +++ b/components/handlebars.js.nuspec @@ -2,7 +2,7 @@ handlebars.js - 4.5.1 + 4.5.2 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 a13ff027c..1fcee3eed 100644 --- a/components/package.json +++ b/components/package.json @@ -1,6 +1,6 @@ { "name": "handlebars", - "version": "4.5.1", + "version": "4.5.2", "license": "MIT", "jspm": { "main": "handlebars", diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index fcf71a2b9..adb1547a5 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.5.1'; +export const VERSION = '4.5.2'; export const COMPILER_REVISION = 8; export const LAST_COMPATIBLE_COMPILER_REVISION = 7; diff --git a/package.json b/package.json index 2cb199f32..d753c9b2a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "handlebars", "barename": "handlebars", - "version": "4.5.1", + "version": "4.5.2", "description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration", "homepage": "http://www.handlebarsjs.com/", "keywords": [