Skip to content

Commit fc45615

Browse files
committed
Create Objects without prototypes.
This generally helps mitigate prototype pollution: even if another library allows prototype pollution, ejs will not allow escalating this into Remote Code Execution.
1 parent 15ee698 commit fc45615

File tree

1 file changed

+13
-10
lines changed

1 file changed

+13
-10
lines changed

lib/ejs.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ function fileLoader(filePath){
306306
*/
307307

308308
function includeFile(path, options) {
309-
var opts = utils.shallowCopy({}, options);
309+
var opts = utils.shallowCopy(Object.create(null), options);
310310
opts.filename = getIncludePath(path, opts);
311311
if (typeof options.includer === 'function') {
312312
var includerResult = options.includer(path, opts.filename);
@@ -412,8 +412,8 @@ exports.compile = function compile(template, opts) {
412412
*/
413413

414414
exports.render = function (template, d, o) {
415-
var data = d || {};
416-
var opts = o || {};
415+
var data = d || Object.create(null);
416+
var opts = o || Object.create(null);
417417

418418
// No options object -- if there are optiony names
419419
// in the data, copy them to options
@@ -484,7 +484,7 @@ exports.renderFile = function () {
484484
opts.filename = filename;
485485
}
486486
else {
487-
data = {};
487+
data = Object.create(null);
488488
}
489489

490490
return tryHandleCache(opts, data, cb);
@@ -506,8 +506,8 @@ exports.clearCache = function () {
506506
};
507507

508508
function Template(text, opts) {
509-
opts = opts || {};
510-
var options = {};
509+
opts = opts || Object.create(null);
510+
var options = Object.create(null);
511511
this.templateText = text;
512512
/** @type {string | null} */
513513
this.mode = null;
@@ -597,7 +597,8 @@ Template.prototype = {
597597
throw new Error('localsName is not a valid JS identifier.');
598598
}
599599
if (opts.destructuredLocals && opts.destructuredLocals.length) {
600-
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
600+
var destructuring = ' var __locals = (' + opts.localsName +
601+
' || Object.create(null)),\n';
601602
for (var i = 0; i < opts.destructuredLocals.length; i++) {
602603
var name = opts.destructuredLocals[i];
603604
if (!_JS_IDENTIFIER.test(name)) {
@@ -611,7 +612,8 @@ Template.prototype = {
611612
prepended += destructuring + ';\n';
612613
}
613614
if (opts._with !== false) {
614-
prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
615+
prepended += ' with (' + opts.localsName
616+
+ ' || Object.create(null)) {' + '\n';
615617
appended += ' }' + '\n';
616618
}
617619
appended += ' return __output;' + '\n';
@@ -693,13 +695,14 @@ Template.prototype = {
693695
// Adds a local `include` function which allows full recursive include
694696
var returnedFn = opts.client ? fn : function anonymous(data) {
695697
var include = function (path, includeData) {
696-
var d = utils.shallowCopy({}, data);
698+
var d = utils.shallowCopy(Object.create(null), data);
697699
if (includeData) {
698700
d = utils.shallowCopy(d, includeData);
699701
}
700702
return includeFile(path, opts)(d);
701703
};
702-
return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
704+
return fn.apply(opts.context,
705+
[data || Object.create(null), escapeFn, include, rethrow]);
703706
};
704707
if (opts.filename && typeof Object.defineProperty === 'function') {
705708
var filename = opts.filename;

0 commit comments

Comments
 (0)