Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1988878

Browse filesBrowse files
committed
fix: add more properties required to be enumerable
- __defineGetter__, __defineSetter__, __lookupGetter__, __proto__
1 parent 886ba86 commit 1988878
Copy full SHA for 1988878

File tree

Expand file treeCollapse file tree

5 files changed

+49
-29
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+49
-29
lines changed

‎lib/handlebars/compiler/compiler.js

Copy file name to clipboardExpand all lines: lib/handlebars/compiler/compiler.js
+2-13Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ Compiler.prototype = {
5454

5555
options.blockParams = options.blockParams || [];
5656

57-
// These changes will propagate to the other compiler components
58-
let knownHelpers = options.knownHelpers;
59-
options.knownHelpers = {
57+
options.knownHelpers = extend(Object.create(null), {
6058
'helperMissing': true,
6159
'blockHelperMissing': true,
6260
'each': true,
@@ -65,15 +63,7 @@ Compiler.prototype = {
6563
'with': true,
6664
'log': true,
6765
'lookup': true
68-
};
69-
if (knownHelpers) {
70-
// the next line should use "Object.keys", but the code has been like this a long time and changing it, might
71-
// cause backwards-compatibility issues... It's an old library...
72-
// eslint-disable-next-line guard-for-in
73-
for (let name in knownHelpers) {
74-
this.options.knownHelpers[name] = knownHelpers[name];
75-
}
76-
}
66+
}, options.knownHelpers);
7767

7868
return this.accept(program);
7969
},
@@ -369,7 +359,6 @@ Compiler.prototype = {
369359
if (isEligible && !isHelper) {
370360
let name = sexpr.path.parts[0],
371361
options = this.options;
372-
373362
if (options.knownHelpers[name]) {
374363
isHelper = true;
375364
} else if (options.knownHelpersOnly) {

‎lib/handlebars/compiler/javascript-compiler.js

Copy file name to clipboardExpand all lines: lib/handlebars/compiler/javascript-compiler.js
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
22
import Exception from '../exception';
33
import {isArray} from '../utils';
44
import CodeGen from './code-gen';
5+
import {dangerousPropertyRegex} from '../helpers/lookup';
56

67
function Literal(value) {
78
this.value = value;
@@ -13,9 +14,8 @@ JavaScriptCompiler.prototype = {
1314
// PUBLIC API: You can override these methods in a subclass to provide
1415
// alternative compiled forms for name lookup and buffering semantics
1516
nameLookup: function(parent, name/* , type*/) {
16-
const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',"constructor")'];
17-
18-
if (name === 'constructor') {
17+
if (dangerousPropertyRegex.test(name)) {
18+
const isEnumerable = [ this.aliasable('container.propertyIsEnumerable'), '.call(', parent, ',', JSON.stringify(name), ')'];
1919
return ['(', isEnumerable, '?', _actualLookup(), ' : undefined)'];
2020
}
2121
return _actualLookup();

‎lib/handlebars/helpers/lookup.js

Copy file name to clipboardExpand all lines: lib/handlebars/helpers/lookup.js
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
export const dangerousPropertyRegex = /^(constructor|__defineGetter__|__defineSetter__|__lookupGetter__|__proto__)$/;
2+
13
export default function(instance) {
24
instance.registerHelper('lookup', function(obj, field) {
35
if (!obj) {
46
return obj;
57
}
6-
if (String(field) === 'constructor' && !obj.propertyIsEnumerable(field)) {
8+
if (dangerousPropertyRegex.test(String(field)) && !obj.propertyIsEnumerable(field)) {
79
return undefined;
810
}
911
return obj[field];

‎spec/env/common.js

Copy file name to clipboardExpand all lines: spec/env/common.js
+20-9Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,24 +142,35 @@ HandlebarsTestBench.prototype.withMessage = function(message) {
142142
};
143143

144144
HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
145+
expect(this._compileAndExeute()).to.equal(expectedOutputAsString);
146+
};
147+
148+
// see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw)
149+
HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher, msg) {
145150
var self = this;
151+
expect(function() {
152+
self._compileAndExeute();
153+
}).to.throw(errorLike, errMsgMatcher, msg);
154+
};
155+
156+
HandlebarsTestBench.prototype._compileAndExeute = function() {
146157
var compile = Object.keys(this.partials).length > 0
147158
? CompilerContext.compileWithPartial
148159
: CompilerContext.compile;
149160

161+
var combinedRuntimeOptions = this._combineRuntimeOptions();
162+
163+
var template = compile(this.templateAsString, this.compileOptions);
164+
return template(this.input, combinedRuntimeOptions);
165+
};
166+
167+
HandlebarsTestBench.prototype._combineRuntimeOptions = function() {
168+
var self = this;
150169
var combinedRuntimeOptions = {};
151170
Object.keys(this.runtimeOptions).forEach(function(key) {
152171
combinedRuntimeOptions[key] = self.runtimeOptions[key];
153172
});
154173
combinedRuntimeOptions.helpers = this.helpers;
155174
combinedRuntimeOptions.partials = this.partials;
156-
157-
var template = compile(this.templateAsString, this.compileOptions);
158-
var output = template(this.input, combinedRuntimeOptions);
159-
160-
if (output !== expectedOutputAsString) {
161-
// Error message formatted so that IntelliJ-Idea shows "diff"-button
162-
// https://stackoverflow.com/a/10945655/4251384
163-
throw new AssertError(this.message + '\nexpected:' + expectedOutputAsString + 'but was:' + output);
164-
}
175+
return combinedRuntimeOptions;
165176
};

‎spec/security.js

Copy file name to clipboardExpand all lines: spec/security.js
+21-3Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,31 @@ describe('security issues', function() {
114114
describe('GH-1563', function() {
115115
it('should not allow to access constructor after overriding via __defineGetter__', function() {
116116
if (({}).__defineGetter__ == null || ({}).__lookupGetter__ == null) {
117-
return; // Browser does not support this exploit anyway
117+
return this.skip(); // Browser does not support this exploit anyway
118118
}
119-
shouldCompileTo('{{__defineGetter__ "undefined" valueOf }}' +
119+
expectTemplate('{{__defineGetter__ "undefined" valueOf }}' +
120120
'{{#with __lookupGetter__ }}' +
121121
'{{__defineGetter__ "propertyIsEnumerable" (this.bind (this.bind 1)) }}' +
122122
'{{constructor.name}}' +
123-
'{{/with}}', {}, '');
123+
'{{/with}}')
124+
.withInput({})
125+
.toThrow(/Missing helper: "__defineGetter__"/);
124126
});
125127
});
128+
129+
describe('GH-1595', function() {
130+
it('properties, that are required to be enumerable', function() {
131+
expectTemplate('{{constructor}}').withInput({}).toCompileTo('');
132+
expectTemplate('{{__defineGetter__}}').withInput({}).toCompileTo('');
133+
expectTemplate('{{__defineSetter__}}').withInput({}).toCompileTo('');
134+
expectTemplate('{{__lookupGetter__}}').withInput({}).toCompileTo('');
135+
expectTemplate('{{__proto__}}').withInput({}).toCompileTo('');
136+
137+
expectTemplate('{{lookup "constructor"}}').withInput({}).toCompileTo('');
138+
expectTemplate('{{lookup "__defineGetter__"}}').withInput({}).toCompileTo('');
139+
expectTemplate('{{lookup "__defineSetter__"}}').withInput({}).toCompileTo('');
140+
expectTemplate('{{lookup "__lookupGetter__"}}').withInput({}).toCompileTo('');
141+
expectTemplate('{{lookup "__proto__"}}').withInput({}).toCompileTo('');
142+
});
143+
});
126144
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.