diff --git a/.eslintrc.js b/.eslintrc.js
index 3ba42305..5c55b10f 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -57,6 +57,7 @@ module.exports = {
"no-with": "error",
"radix": "error",
"wrap-iife": "error",
+ "no-prototype-builtins": "error",
// Variables //
@@ -114,4 +115,4 @@ module.exports = {
"ecmaVersion": 6,
"ecmaFeatures": {}
}
-}
+};
diff --git a/bench/util/benchwarmer.js b/bench/util/benchwarmer.js
index 49f66f73..58f138a0 100644
--- a/bench/util/benchwarmer.js
+++ b/bench/util/benchwarmer.js
@@ -29,7 +29,7 @@ BenchWarmer.prototype = {
});
},
push: function(name, fn) {
- if (this.names.indexOf(name) == -1) {
+ if (this.names.indexOf(name) === -1) {
this.names.push(name);
}
@@ -77,7 +77,7 @@ BenchWarmer.prototype = {
var errors = false, prop, bench;
for (prop in self.errors) {
- if (self.errors.hasOwnProperty(prop)
+ if (Object.prototype.hasOwnProperty.call(self, prop)
&& self.errors[prop].error.message !== 'EWOT') {
errors = true;
break;
@@ -86,9 +86,8 @@ BenchWarmer.prototype = {
if (errors) {
print('\n\nErrors:\n');
- for (prop in self.errors) {
- if (self.errors.hasOwnProperty(prop)
- && self.errors[prop].error.message !== 'EWOT') {
+ Object.keys(self.errors).forEach(function(prop) {
+ if (self.errors[prop].error.message !== 'EWOT') {
bench = self.errors[prop];
print('\n' + bench.name + ':\n');
print(bench.error.message);
@@ -97,7 +96,7 @@ BenchWarmer.prototype = {
}
print('\n');
}
- }
+ });
}
callback();
diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js
index adb1547a..ad3541b5 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.2';
+export const VERSION = '4.5.3';
export const COMPILER_REVISION = 8;
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
diff --git a/lib/handlebars/compiler/code-gen.js b/lib/handlebars/compiler/code-gen.js
index 43c0481b..30c5af74 100644
--- a/lib/handlebars/compiler/code-gen.js
+++ b/lib/handlebars/compiler/code-gen.js
@@ -125,14 +125,12 @@ CodeGen.prototype = {
objectLiteral: function(obj) {
let pairs = [];
- for (let key in obj) {
- if (obj.hasOwnProperty(key)) {
- let value = castChunk(obj[key], this);
- if (value !== 'undefined') {
- pairs.push([this.quotedString(key), ':', value]);
- }
+ Object.keys(obj).forEach(key => {
+ let value = castChunk(obj[key], this);
+ if (value !== 'undefined') {
+ pairs.push([this.quotedString(key), ':', value]);
}
- }
+ });
let ret = this.generateList(pairs);
ret.prepend('{');
diff --git a/lib/handlebars/compiler/compiler.js b/lib/handlebars/compiler/compiler.js
index d5e78d82..b7a2f1c2 100644
--- a/lib/handlebars/compiler/compiler.js
+++ b/lib/handlebars/compiler/compiler.js
@@ -52,9 +52,7 @@ Compiler.prototype = {
options.blockParams = options.blockParams || [];
- // These changes will propagate to the other compiler components
- let knownHelpers = options.knownHelpers;
- options.knownHelpers = {
+ options.knownHelpers = extend(Object.create(null), {
'helperMissing': true,
'blockHelperMissing': true,
'each': true,
@@ -63,15 +61,7 @@ Compiler.prototype = {
'with': true,
'log': true,
'lookup': true
- };
- if (knownHelpers) {
- // the next line should use "Object.keys", but the code has been like this a long time and changing it, might
- // cause backwards-compatibility issues... It's an old library...
- // eslint-disable-next-line guard-for-in
- for (let name in knownHelpers) {
- this.options.knownHelpers[name] = knownHelpers[name];
- }
- }
+ }, options.knownHelpers);
return this.accept(program);
},
@@ -367,7 +357,6 @@ Compiler.prototype = {
if (isEligible && !isHelper) {
let name = sexpr.path.parts[0],
options = this.options;
-
if (options.knownHelpers[name]) {
isHelper = true;
} else if (options.knownHelpersOnly) {
diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js
index f7c4b5c3..066f4ba5 100644
--- a/lib/handlebars/compiler/javascript-compiler.js
+++ b/lib/handlebars/compiler/javascript-compiler.js
@@ -2,6 +2,7 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
import Exception from '../exception';
import {isArray} from '../utils';
import CodeGen from './code-gen';
+import {dangerousPropertyRegex} from '../helpers/lookup';
function Literal(value) {
this.value = value;
@@ -13,9 +14,8 @@ 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*/) {
- const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',"constructor")'];
-
- if (name === 'constructor') {
+ if (dangerousPropertyRegex.test(name)) {
+ const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',', JSON.stringify(name), ')'];
return ['(', isEnumerable, '?', _actualLookup(), ' : undefined)'];
}
return _actualLookup();
@@ -216,13 +216,13 @@ JavaScriptCompiler.prototype = {
// aliases will not be used, but this case is already being run on the client and
// we aren't concern about minimizing the template size.
let aliasCount = 0;
- for (let alias in this.aliases) { // eslint-disable-line guard-for-in
+ Object.keys(this.aliases).forEach(alias => {
let node = this.aliases[alias];
- if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) {
+ if (node.children && node.referenceCount > 1) {
varDeclarations += ', alias' + (++aliasCount) + '=' + alias;
node.children[0] = 'alias' + aliasCount;
}
- }
+ });
let params = ['container', 'depth0', 'helpers', 'partials', 'data'];
diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js
index 5bb4c903..f65a6aff 100644
--- a/lib/handlebars/helpers/each.js
+++ b/lib/handlebars/helpers/each.js
@@ -53,18 +53,16 @@ export default function(instance) {
} else {
let priorKey;
- for (let key in context) {
- if (context.hasOwnProperty(key)) {
- // We're running the iterations one step out of sync so we can detect
- // the last iteration without have to scan the object twice and create
- // an itermediate keys array.
- if (priorKey !== undefined) {
- execIteration(priorKey, i - 1);
- }
- priorKey = key;
- i++;
+ Object.keys(context).forEach(key => {
+ // We're running the iterations one step out of sync so we can detect
+ // the last iteration without have to scan the object twice and create
+ // an itermediate keys array.
+ if (priorKey !== undefined) {
+ execIteration(priorKey, i - 1);
}
- }
+ priorKey = key;
+ i++;
+ });
if (priorKey !== undefined) {
execIteration(priorKey, i - 1, true);
}
diff --git a/lib/handlebars/helpers/lookup.js b/lib/handlebars/helpers/lookup.js
index 0654cc39..8c1b604b 100644
--- a/lib/handlebars/helpers/lookup.js
+++ b/lib/handlebars/helpers/lookup.js
@@ -1,9 +1,11 @@
+export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/;
+
export default function(instance) {
instance.registerHelper('lookup', function(obj, field) {
if (!obj) {
return obj;
}
- if (String(field) === 'constructor' && !obj.propertyIsEnumerable(field)) {
+ if (dangerousPropertyRegex.test(String(field)) && !Object.prototype.propertyIsEnumerable.call(obj, field)) {
return undefined;
}
return obj[field];
diff --git a/package-lock.json b/package-lock.json
index 737a5257..7f27c1dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -83,6 +83,50 @@
}
}
},
+ "@sinonjs/commons": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz",
+ "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ },
+ "@sinonjs/formatio": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz",
+ "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1",
+ "@sinonjs/samsam": "^3.1.0"
+ }
+ },
+ "@sinonjs/samsam": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz",
+ "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.3.0",
+ "array-from": "^2.1.1",
+ "lodash": "^4.17.15"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ }
+ }
+ },
+ "@sinonjs/text-encoding": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz",
+ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
+ "dev": true
+ },
"@types/parsimmon": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.0.tgz",
@@ -276,6 +320,12 @@
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
"dev": true
},
+ "array-from": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz",
+ "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
+ "dev": true
+ },
"array-slice": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
@@ -329,6 +379,12 @@
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -990,6 +1046,20 @@
"lazy-cache": "^1.0.3"
}
},
+ "chai": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "pathval": "^1.1.0",
+ "type-detect": "^4.0.5"
+ }
+ },
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
@@ -1009,6 +1079,12 @@
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=",
"dev": true
},
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "dev": true
+ },
"chokidar": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.5.2.tgz",
@@ -1479,6 +1555,15 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -1647,6 +1732,12 @@
"integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=",
"dev": true
},
+ "dirty-chai": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-2.0.1.tgz",
+ "integrity": "sha512-ys79pWKvDMowIDEPC6Fig8d5THiC0DJ2gmTeGzVAoEH18J8OzLud0Jh7I9IWg3NSk8x2UocznUuFmfHCXYZx9w==",
+ "dev": true
+ },
"doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -3321,6 +3412,12 @@
"integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=",
"dev": true
},
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "dev": true
+ },
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@@ -4888,6 +4985,12 @@
"verror": "1.10.0"
}
},
+ "just-extend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz",
+ "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==",
+ "dev": true
+ },
"kind-of": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
@@ -5327,6 +5430,12 @@
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
"dev": true
},
+ "lolex": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz",
+ "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==",
+ "dev": true
+ },
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@@ -5781,6 +5890,36 @@
"integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=",
"dev": true
},
+ "nise": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz",
+ "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/formatio": "^3.2.1",
+ "@sinonjs/text-encoding": "^0.7.1",
+ "just-extend": "^4.0.2",
+ "lolex": "^4.1.0",
+ "path-to-regexp": "^1.7.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+ "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ }
+ }
+ },
"node-libs-browser": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz",
@@ -6368,6 +6507,12 @@
"pinkie-promise": "^2.0.0"
}
},
+ "pathval": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+ "dev": true
+ },
"pbkdf2-compat": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz",
@@ -7459,6 +7604,32 @@
"integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=",
"dev": true
},
+ "sinon": {
+ "version": "7.5.0",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz",
+ "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==",
+ "dev": true,
+ "requires": {
+ "@sinonjs/commons": "^1.4.0",
+ "@sinonjs/formatio": "^3.2.1",
+ "@sinonjs/samsam": "^3.3.3",
+ "diff": "^3.5.0",
+ "lolex": "^4.2.0",
+ "nise": "^1.5.2",
+ "supports-color": "^5.5.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -8289,6 +8460,12 @@
"prelude-ls": "~1.1.2"
}
},
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
diff --git a/package.json b/package.json
index afef714e..231c9215 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,8 @@
"babel-loader": "^5.0.0",
"babel-runtime": "^5.1.10",
"benchmark": "~1.0",
+ "chai": "^4.2.0",
+ "dirty-chai": "^2.0.1",
"dtslint": "^0.5.5",
"dustjs-linkedin": "^2.0.2",
"eco": "~1.1.0-rc-3",
@@ -58,6 +60,7 @@
"mock-stdin": "^0.3.0",
"mustache": "^2.1.3",
"semver": "^5.0.1",
+ "sinon": "^7.5.0",
"typescript": "^3.4.3",
"underscore": "^1.5.1",
"webpack": "^1.12.6",
diff --git a/release-notes.md b/release-notes.md
index 3eefabe8..199c39bf 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,49 @@
## Development
-[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.2...master)
+[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.3...master)
+
+## v4.5.3 - November 18th, 2019
+Bugfixes:
+
+- fix: add "no-prototype-builtins" eslint-rule and fix all occurences - f7f05d7
+- fix: add more properties required to be enumerable - 1988878
+
+Chores / Build:
+- fix: use !== 0 instead of != 0 - c02b05f
+- add chai and dirty-chai and sinon, for cleaner test-assertions and spies,
+ deprecate old assertion-methods - 93e284e, 886ba86, 0817dad, 93516a0
+
+Security:
+
+- The properties `__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__`
+ have been added to the list of "properties that must be enumerable".
+ If a property by that name is found and not enumerable on its parent,
+ it will silently evaluate to `undefined`. This is done in both the compiled template and the "lookup"-helper.
+ This will prevent new Remote-Code-Execution exploits that have been
+ published recently.
+
+Compatibility notes:
+
+- Due to the security-fixes. The semantics of the templates using
+ `__proto__`, `__defineGetter__`, `__defineSetter__` and `__lookupGetter__` in the respect that those expression now return
+ `undefined` rather than their actual value from the proto.
+- The semantics have not changed in cases where the properties are enumerable, as in:
+
+```js
+{
+ __proto__: 'some string'
+}
+```
+
+- The change may be breaking in that respect, but we still only
+ increase the patch-version, because the incompatible use-cases
+ are not intended, undocumented and far less important than fixing
+ Remote-Code-Execution exploits on existing systems.
+
+
+
+[Commits](https://github.com/wycats/handlebars.js/compare/v4.5.2...v4.5.3)
## v4.5.2 - November 13th, 2019
# Bugfixes
diff --git a/spec/.eslintrc b/spec/.eslintrc
index 34319f8c..4bec97f4 100644
--- a/spec/.eslintrc
+++ b/spec/.eslintrc
@@ -27,16 +27,20 @@
"start": true,
"stop": true,
"ok": true,
+ "sinon": true,
"strictEqual": true,
- "define": true
+ "define": true,
+ "expect": true,
+ "chai": true
},
"env": {
"mocha": true
},
"rules": {
// Disabling for tests, for now.
- "no-path-concat": 0,
+ "no-path-concat": "off",
- "no-var": 0
+ "no-var": "off",
+ "dot-notation": "off"
}
}
\ No newline at end of file
diff --git a/spec/compiler.js b/spec/compiler.js
index 3489df4e..7734bccf 100644
--- a/spec/compiler.js
+++ b/spec/compiler.js
@@ -59,7 +59,7 @@ describe('compiler', function() {
Handlebars.compile(' \n {{#if}}\n{{/def}}')();
equal(true, false, 'Statement must throw exception. This line should not be executed.');
} catch (err) {
- equal(err.propertyIsEnumerable('column'), true, 'Checking error column');
+ equal(Object.prototype.propertyIsEnumerable.call(err, 'column'), true, 'Checking error column');
}
});
diff --git a/spec/env/browser.js b/spec/env/browser.js
index 9e54a397..d1dab0d5 100644
--- a/spec/env/browser.js
+++ b/spec/env/browser.js
@@ -3,6 +3,15 @@ require('./common');
var fs = require('fs'),
vm = require('vm');
+var chai = require('chai');
+var dirtyChai = require('dirty-chai');
+
+
+chai.use(dirtyChai);
+global.expect = chai.expect;
+
+global.sinon = require('sinon');
+
global.Handlebars = 'no-conflict';
var filename = 'dist/handlebars.js';
diff --git a/spec/env/common.js b/spec/env/common.js
index 9523eaeb..ac942184 100644
--- a/spec/env/common.js
+++ b/spec/env/common.js
@@ -63,12 +63,20 @@ global.compileWithPartials = function(string, hashOrArray, partials) {
};
+/**
+ * @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`)
+ * @see https://www.chaijs.com/api/bdd/
+ */
global.equals = global.equal = function equals(a, b, msg) {
if (a !== b) {
throw new AssertError("'" + a + "' should === '" + b + "'" + (msg ? ': ' + msg : ''), equals);
}
};
+/**
+ * @deprecated Use chai's expect-style API instead (`expect(actualValue).to.equal(expectedValue)`)
+ * @see https://www.chaijs.com/api/bdd/#method_throw
+ */
global.shouldThrow = function(callback, type, msg) {
var failed;
try {
@@ -134,24 +142,35 @@ HandlebarsTestBench.prototype.withMessage = function(message) {
};
HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
+ expect(this._compileAndExeute()).to.equal(expectedOutputAsString);
+};
+
+// see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw)
+HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher, msg) {
var self = this;
+ expect(function() {
+ self._compileAndExeute();
+ }).to.throw(errorLike, errMsgMatcher, msg);
+};
+
+HandlebarsTestBench.prototype._compileAndExeute = function() {
var compile = Object.keys(this.partials).length > 0
? CompilerContext.compileWithPartial
: CompilerContext.compile;
+ var combinedRuntimeOptions = this._combineRuntimeOptions();
+
+ var template = compile(this.templateAsString, this.compileOptions);
+ return template(this.input, combinedRuntimeOptions);
+};
+
+HandlebarsTestBench.prototype._combineRuntimeOptions = function() {
+ var self = this;
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);
- }
+ return combinedRuntimeOptions;
};
diff --git a/spec/env/node.js b/spec/env/node.js
index 881609d2..0695468b 100644
--- a/spec/env/node.js
+++ b/spec/env/node.js
@@ -1,5 +1,14 @@
require('./common');
+var chai = require('chai');
+var dirtyChai = require('dirty-chai');
+
+
+chai.use(dirtyChai);
+global.expect = chai.expect;
+
+global.sinon = require('sinon');
+
global.Handlebars = require('../../lib');
global.CompilerContext = {
diff --git a/spec/env/runtime.js b/spec/env/runtime.js
index 642acd34..4f6d2853 100644
--- a/spec/env/runtime.js
+++ b/spec/env/runtime.js
@@ -3,6 +3,15 @@ require('./common');
var fs = require('fs'),
vm = require('vm');
+var chai = require('chai');
+var dirtyChai = require('dirty-chai');
+
+
+chai.use(dirtyChai);
+global.expect = chai.expect;
+
+global.sinon = require('sinon');
+
global.Handlebars = 'no-conflict';
var filename = 'dist/handlebars.runtime.js';
diff --git a/spec/index.html b/spec/index.html
index 5db2146a..b768dff7 100644
--- a/spec/index.html
+++ b/spec/index.html
@@ -19,12 +19,15 @@
document.documentElement.className = 'headless';
}
+
+
+
-
+
+
+
-
diff --git a/spec/umd.html b/spec/umd.html
index 5f3863fc..53d6b271 100644
--- a/spec/umd.html
+++ b/spec/umd.html
@@ -18,11 +18,15 @@
document.documentElement.className = 'headless';
}
+
+
+
+
-
diff --git a/tasks/test.js b/tasks/test.js
index 6bee3920..a17fc2a2 100644
--- a/tasks/test.js
+++ b/tasks/test.js
@@ -33,7 +33,7 @@ module.exports = function(grunt) {
var runner = childProcess.fork('./spec/env/runner', [], {stdio: 'inherit'});
runner.on('close', function(code) {
- if (code != 0) {
+ if (code !== 0) {
grunt.fatal(code + ' tests failed');
}
done();
@@ -55,7 +55,7 @@ module.exports = function(grunt) {
var runner = childProcess.fork('./spec/env/runner', ['--min'], {stdio: 'inherit'});
runner.on('close', function(code) {
- if (code != 0) {
+ if (code !== 0) {
grunt.fatal(code + ' tests failed');
}
done();