diff --git a/lib/ejs.js b/lib/ejs.js index 98e5991c..04b9bcb6 100644 --- a/lib/ejs.js +++ b/lib/ejs.js @@ -56,6 +56,12 @@ var _REGEX_STRING = '(<%%|%%>|<%=|<%-|<%_|<%#|<%|%>|-%>|_%>)'; var _OPTS = [ 'cache', 'filename', 'delimiter', 'scope', 'context', 'debug', 'compileDebug', 'client', '_with', 'root', 'rmWhitespace', 'strict', 'localsName']; +var _OPTS_IN_DATA_BLACKLIST = { + cache: true, + filename: true, + root: true, + localsName: true + }; var _BOM = /^\uFEFF/; /** @@ -268,11 +274,9 @@ function rethrow(err, str, filename, lineno){ function cpOptsInData(data, opts) { _OPTS.forEach(function (p) { if (typeof data[p] != 'undefined') { - // Disallow setting the root opt for includes via a passed data obj - // Unsanitized, parameterized use of `render` could allow the - // include directory to be reset, opening up the possibility of - // remote code execution - if (p == 'root') { + // Disallow passing potentially dangerous opts in the data + // These opts should not be settable via a `render` call + if (_OPTS_IN_DATA_BLACKLIST[p]) { return; } opts[p] = data[p]; diff --git a/test/ejs.js b/test/ejs.js index c2bb87a7..b8cb4c9d 100644 --- a/test/ejs.js +++ b/test/ejs.js @@ -168,6 +168,21 @@ suite('ejs.compile(str, options)', function () { }); +/* Old API -- remove when this shim goes away */ +suite('ejs.render(str, dataAndOpts)', function () { + test('render the template with data/opts passed together', function () { + assert.equal(ejs.render('
= foo ?>
', {foo: 'yay', delimiter: '?'}), + 'yay
'); + }); + + test('disallow unsafe opts passed along in data', function () { + assert.equal(ejs.render('= locals.foo ?>
', + // localsName should not get reset because it's blacklisted + {_with: false, foo: 'yay', delimiter: '?', localsName: '_'}), + 'yay
'); + }); +}); + suite('ejs.render(str, data, opts)', function () { test('render the template', function () { assert.equal(ejs.render('yay
'), 'yay
'); @@ -753,7 +768,6 @@ suite('include()', function () { var viewsPath = path.join(__dirname, 'fixtures'); assert.equal(ejs.render(fixture('include-root.ejs'), {pets: users}, {filename: file, delimiter: '@',root:viewsPath}), fixture('include.html')); - }); test('work when nested', function () { @@ -918,7 +932,7 @@ suite('preprocessor include', function () { var template = fixture('include_preprocessor_line_slurp.ejs'); var expected = fixture('include_preprocessor_line_slurp.html'); var options = {rmWhitespace: true, filename: file}; - assert.equal(ejs.render(template, options), + assert.equal(ejs.render(template, {}, options), expected); });