Skip to content

Commit be9a9bb

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 be9a9bb

File tree

1 file changed

+21
-8
lines changed

1 file changed

+21
-8
lines changed

lib/ejs.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @public
4545
*/
4646

47+
4748
var fs = require('fs');
4849
var path = require('path');
4950
var utils = require('./utils');
@@ -66,6 +67,17 @@ var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');
6667
var _BOM = /^\uFEFF/;
6768
var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
6869

70+
var createObj = function() {
71+
if (typeof Object.create !== 'function') {
72+
return function (o) {
73+
function F() {}
74+
F.prototype = o;
75+
return new F();
76+
};
77+
}
78+
return Object.create;
79+
}();
80+
6981
/**
7082
* EJS template function cache. This can be a LRU object from lru-cache NPM
7183
* module. By default, it is {@link module:utils.cache}, a simple in-process
@@ -306,7 +318,7 @@ function fileLoader(filePath){
306318
*/
307319

308320
function includeFile(path, options) {
309-
var opts = utils.shallowCopy({}, options);
321+
var opts = utils.shallowCopy(createObj(null), options);
310322
opts.filename = getIncludePath(path, opts);
311323
if (typeof options.includer === 'function') {
312324
var includerResult = options.includer(path, opts.filename);
@@ -412,8 +424,8 @@ exports.compile = function compile(template, opts) {
412424
*/
413425

414426
exports.render = function (template, d, o) {
415-
var data = d || {};
416-
var opts = o || {};
427+
var data = d || createObj(null);
428+
var opts = o || createObj(null);
417429

418430
// No options object -- if there are optiony names
419431
// in the data, copy them to options
@@ -484,7 +496,7 @@ exports.renderFile = function () {
484496
opts.filename = filename;
485497
}
486498
else {
487-
data = {};
499+
data = createObj(null);
488500
}
489501

490502
return tryHandleCache(opts, data, cb);
@@ -506,8 +518,8 @@ exports.clearCache = function () {
506518
};
507519

508520
function Template(text, opts) {
509-
opts = opts || {};
510-
var options = {};
521+
opts = opts || createObj(null);
522+
var options = createObj(null);
511523
this.templateText = text;
512524
/** @type {string | null} */
513525
this.mode = null;
@@ -693,13 +705,14 @@ Template.prototype = {
693705
// Adds a local `include` function which allows full recursive include
694706
var returnedFn = opts.client ? fn : function anonymous(data) {
695707
var include = function (path, includeData) {
696-
var d = utils.shallowCopy({}, data);
708+
var d = utils.shallowCopy(createObj(null), data);
697709
if (includeData) {
698710
d = utils.shallowCopy(d, includeData);
699711
}
700712
return includeFile(path, opts)(d);
701713
};
702-
return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
714+
return fn.apply(opts.context,
715+
[data || createObj(null), escapeFn, include, rethrow]);
703716
};
704717
if (opts.filename && typeof Object.defineProperty === 'function') {
705718
var filename = opts.filename;

0 commit comments

Comments
 (0)