From 156061eb7707575293613d7fdf90e2bdaac029ee Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sun, 23 Feb 2020 00:39:39 +0100 Subject: [PATCH] backport fixes from 4.x --- lib/handlebars/base.js | 2 +- .../compiler/javascript-compiler.js | 19 ++++++++++++------- lib/handlebars/utils.js | 2 ++ spec/security.js | 16 ++++++++++++++++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/lib/handlebars/base.js b/lib/handlebars/base.js index 5342f294f..3a138e4d4 100644 --- a/lib/handlebars/base.js +++ b/lib/handlebars/base.js @@ -215,7 +215,7 @@ function registerDefaultHelpers(instance) { if (!obj) { return obj; } - if (field === 'constructor' && !obj.propertyIsEnumerable(field)) { + if (Utils.dangerousPropertyRegex.test(String(field)) && !Object.prototype.hasOwnProperty.call(obj, field)) { return undefined; } return obj[field]; diff --git a/lib/handlebars/compiler/javascript-compiler.js b/lib/handlebars/compiler/javascript-compiler.js index f070a39f9..d129815c0 100644 --- a/lib/handlebars/compiler/javascript-compiler.js +++ b/lib/handlebars/compiler/javascript-compiler.js @@ -1,6 +1,6 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base'; import Exception from '../exception'; -import {isArray} from '../utils'; +import {isArray, dangerousPropertyRegex} from '../utils'; import CodeGen from './code-gen'; function Literal(value) { @@ -13,13 +13,18 @@ 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 (dangerousPropertyRegex.test(name)) { + const isOwnProperty = [ this.aliasable('Object.prototype.hasOwnProperty'), '.call(', parent, ',', JSON.stringify(name), ')']; + return ['(', isOwnProperty, '?', _actualLookup(), ' : undefined)']; } - if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { - return [parent, '.', name]; - } else { - return [parent, "['", name, "']"]; + return _actualLookup(); + + function _actualLookup() { + if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + return [parent, '.', name]; + } else { + return [parent, '[', JSON.stringify(name), ']']; + } } }, depthedLookup: function(name) { diff --git a/lib/handlebars/utils.js b/lib/handlebars/utils.js index b84df49c9..91742c540 100644 --- a/lib/handlebars/utils.js +++ b/lib/handlebars/utils.js @@ -101,3 +101,5 @@ export function blockParams(params, ids) { export function appendContextPath(contextPath, id) { return (contextPath ? contextPath + '.' : '') + id; } + +export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/; diff --git a/spec/security.js b/spec/security.js index 5af5334eb..5b72ad53c 100644 --- a/spec/security.js +++ b/spec/security.js @@ -30,4 +30,20 @@ describe('security issues', function() { new TestClass(), 'xyz'); }); }); + + describe('GH-1595', function() { + it('properties, that are required to be enumerable', function() { + shouldCompileTo('{{constructor.name}}', {}, ''); + shouldCompileTo('{{__defineGetter__.name}}', {}, ''); + shouldCompileTo('{{__defineSetter__.name}}', {}, ''); + shouldCompileTo('{{__lookupGetter__.name}}', {}, ''); + shouldCompileTo('{{__proto__.__defineGetter__.name}}', {}, ''); + + shouldCompileTo('{{lookup this "constructor"}}', {}, ''); + shouldCompileTo('{{lookup this "__defineGetter__"}}', {}, ''); + shouldCompileTo('{{lookup this "__defineSetter__"}}', {}, ''); + shouldCompileTo('{{lookup this "__lookupGetter__"}}', {}, ''); + shouldCompileTo('{{lookup this "__proto__"}}', {}, ''); + }); + }); });