Skip to content

Commit 156061e

Browse files
committed
backport fixes from 4.x
1 parent 8ba9159 commit 156061e

File tree

4 files changed

+31
-8
lines changed

4 files changed

+31
-8
lines changed

lib/handlebars/base.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ function registerDefaultHelpers(instance) {
215215
if (!obj) {
216216
return obj;
217217
}
218-
if (field === 'constructor' && !obj.propertyIsEnumerable(field)) {
218+
if (Utils.dangerousPropertyRegex.test(String(field)) && !Object.prototype.hasOwnProperty.call(obj, field)) {
219219
return undefined;
220220
}
221221
return obj[field];

lib/handlebars/compiler/javascript-compiler.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
22
import Exception from '../exception';
3-
import {isArray} from '../utils';
3+
import {isArray, dangerousPropertyRegex} from '../utils';
44
import CodeGen from './code-gen';
55

66
function Literal(value) {
@@ -13,13 +13,18 @@ JavaScriptCompiler.prototype = {
1313
// PUBLIC API: You can override these methods in a subclass to provide
1414
// alternative compiled forms for name lookup and buffering semantics
1515
nameLookup: function(parent, name /* , type*/) {
16-
if (name === 'constructor') {
17-
return ['(', parent, '.propertyIsEnumerable(\'constructor\') ? ', parent, '.constructor : undefined', ')'];
16+
if (dangerousPropertyRegex.test(name)) {
17+
const isOwnProperty = [ this.aliasable('Object.prototype.hasOwnProperty'), '.call(', parent, ',', JSON.stringify(name), ')'];
18+
return ['(', isOwnProperty, '?', _actualLookup(), ' : undefined)'];
1819
}
19-
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
20-
return [parent, '.', name];
21-
} else {
22-
return [parent, "['", name, "']"];
20+
return _actualLookup();
21+
22+
function _actualLookup() {
23+
if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
24+
return [parent, '.', name];
25+
} else {
26+
return [parent, '[', JSON.stringify(name), ']'];
27+
}
2328
}
2429
},
2530
depthedLookup: function(name) {

lib/handlebars/utils.js

+2
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,5 @@ export function blockParams(params, ids) {
101101
export function appendContextPath(contextPath, id) {
102102
return (contextPath ? contextPath + '.' : '') + id;
103103
}
104+
105+
export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/;

spec/security.js

+16
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,20 @@ describe('security issues', function() {
3030
new TestClass(), 'xyz');
3131
});
3232
});
33+
34+
describe('GH-1595', function() {
35+
it('properties, that are required to be enumerable', function() {
36+
shouldCompileTo('{{constructor.name}}', {}, '');
37+
shouldCompileTo('{{__defineGetter__.name}}', {}, '');
38+
shouldCompileTo('{{__defineSetter__.name}}', {}, '');
39+
shouldCompileTo('{{__lookupGetter__.name}}', {}, '');
40+
shouldCompileTo('{{__proto__.__defineGetter__.name}}', {}, '');
41+
42+
shouldCompileTo('{{lookup this "constructor"}}', {}, '');
43+
shouldCompileTo('{{lookup this "__defineGetter__"}}', {}, '');
44+
shouldCompileTo('{{lookup this "__defineSetter__"}}', {}, '');
45+
shouldCompileTo('{{lookup this "__lookupGetter__"}}', {}, '');
46+
shouldCompileTo('{{lookup this "__proto__"}}', {}, '');
47+
});
48+
});
3349
});

0 commit comments

Comments
 (0)