Skip to content

Commit

Permalink
Create Objects without prototypes.
Browse files Browse the repository at this point in the history
This generally helps mitigate prototype pollution: even if another
library allows prototype pollution, ejs will not allow escalating this
into Remote Code Execution.
  • Loading branch information
nicdumz committed May 31, 2021
1 parent 15ee698 commit ced5d04
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 10 deletions.
25 changes: 15 additions & 10 deletions lib/ejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
* @public
*/

require('core-js/features/object/create');

var fs = require('fs');
var path = require('path');
var utils = require('./utils');
Expand Down Expand Up @@ -306,7 +308,7 @@ function fileLoader(filePath){
*/

function includeFile(path, options) {
var opts = utils.shallowCopy({}, options);
var opts = utils.shallowCopy(Object.create(null), options);
opts.filename = getIncludePath(path, opts);
if (typeof options.includer === 'function') {
var includerResult = options.includer(path, opts.filename);
Expand Down Expand Up @@ -412,8 +414,8 @@ exports.compile = function compile(template, opts) {
*/

exports.render = function (template, d, o) {
var data = d || {};
var opts = o || {};
var data = d || Object.create(null);
var opts = o || Object.create(null);

// No options object -- if there are optiony names
// in the data, copy them to options
Expand Down Expand Up @@ -484,7 +486,7 @@ exports.renderFile = function () {
opts.filename = filename;
}
else {
data = {};
data = Object.create(null);
}

return tryHandleCache(opts, data, cb);
Expand All @@ -506,8 +508,8 @@ exports.clearCache = function () {
};

function Template(text, opts) {
opts = opts || {};
var options = {};
opts = opts || Object.create(null);
var options = Object.create(null);
this.templateText = text;
/** @type {string | null} */
this.mode = null;
Expand Down Expand Up @@ -597,7 +599,8 @@ Template.prototype = {
throw new Error('localsName is not a valid JS identifier.');
}
if (opts.destructuredLocals && opts.destructuredLocals.length) {
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
var destructuring = ' var __locals = (' + opts.localsName +
' || Object.create(null)),\n';
for (var i = 0; i < opts.destructuredLocals.length; i++) {
var name = opts.destructuredLocals[i];
if (!_JS_IDENTIFIER.test(name)) {
Expand All @@ -611,7 +614,8 @@ Template.prototype = {
prepended += destructuring + ';\n';
}
if (opts._with !== false) {
prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';
prepended += ' with (' + opts.localsName
+ ' || Object.create(null)) {' + '\n';
appended += ' }' + '\n';
}
appended += ' return __output;' + '\n';
Expand Down Expand Up @@ -693,13 +697,14 @@ Template.prototype = {
// Adds a local `include` function which allows full recursive include
var returnedFn = opts.client ? fn : function anonymous(data) {
var include = function (path, includeData) {
var d = utils.shallowCopy({}, data);
var d = utils.shallowCopy(Object.create(null), data);
if (includeData) {
d = utils.shallowCopy(d, includeData);
}
return includeFile(path, opts)(d);
};
return fn.apply(opts.context, [data || {}, escapeFn, include, rethrow]);
return fn.apply(opts.context,
[data || Object.create(null), escapeFn, include, rethrow]);
};
if (opts.filename && typeof Object.defineProperty === 'function') {
var filename = opts.filename;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"bugs": "https://github.com/mde/ejs/issues",
"homepage": "https://github.com/mde/ejs",
"dependencies": {
"core-js": "^3.13.1",
"jake": "^10.6.1"
},
"devDependencies": {
Expand Down

0 comments on commit ced5d04

Please sign in to comment.