Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mde/ejs
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.1.6
Choose a base ref
...
head repository: mde/ejs
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.1.7
Choose a head ref

Commits on Feb 26, 2021

  1. Copy the full SHA
    9f66025 View commit details

Commits on Feb 27, 2021

  1. Merge pull request #584 from db-developer/main

    introduced windows compatibility on tests
    mde authored Feb 27, 2021
    Copy the full SHA
    fcae811 View commit details

Commits on Mar 1, 2021

  1. fixed some situations where null or undefined on 'to', 'from' or 'lis…

    …t' would throw exceptions
    db-developer committed Mar 1, 2021
    Copy the full SHA
    5ea8977 View commit details
  2. Copy the full SHA
    9932bd9 View commit details
  3. Merge pull request #585 from db-developer/main

    fixed some situations...
    mde authored Mar 1, 2021
    Copy the full SHA
    c594d0e View commit details

Commits on May 11, 2021

  1. Bump lodash from 4.17.20 to 4.17.21

    Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
    - [Release notes](https://github.com/lodash/lodash/releases)
    - [Commits](lodash/lodash@4.17.20...4.17.21)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored May 11, 2021
    Copy the full SHA
    cfa2732 View commit details
  2. Merge pull request #598 from mde/dependabot/npm_and_yarn/lodash-4.17.21

    Bump lodash from 4.17.20 to 4.17.21
    mde authored May 11, 2021
    Copy the full SHA
    c120527 View commit details

Commits on May 30, 2021

  1. Sanitize option names.

    This prevents injection of arbitrary code if the server is already
    vulnerable to prototype poisoning. This resolves #451.
    
    I deliberately opted to not support complex Unicode identifiers even
    though they're valid JS identifiers. They're complex to validate and
    users probably shouldn't even try to be that creative.
    nicdumz committed May 30, 2021
    Copy the full SHA
    15ee698 View commit details

Commits on May 31, 2021

  1. 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.
    nicdumz committed May 31, 2021
    Copy the full SHA
    be9a9bb View commit details
  2. Merge pull request #601 from nicdumz/main

    Mitigate prototype pollution effects
    mde authored May 31, 2021
    Copy the full SHA
    61b6616 View commit details
  3. Copy the full SHA
    576283b View commit details

Commits on Jun 5, 2021

  1. Update syntax.md

    akash-55 authored Jun 5, 2021
    Copy the full SHA
    99b2d8e View commit details
  2. Merge pull request #606 from akash-55/main

    Update syntax.md
    mde authored Jun 5, 2021
    Copy the full SHA
    a50e46f View commit details

Commits on Jun 6, 2021

  1. Merge pull request #603 from mde/mde-null-proto-where-possible

    Move to utils, handle older runtimes, fix tests
    mde authored Jun 6, 2021
    Copy the full SHA
    f21a9e4 View commit details

Commits on Jun 11, 2021

  1. Bump glob-parent from 5.1.1 to 5.1.2

    Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
    - [Release notes](https://github.com/gulpjs/glob-parent/releases)
    - [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
    - [Commits](gulpjs/glob-parent@v5.1.1...v5.1.2)
    
    ---
    updated-dependencies:
    - dependency-name: glob-parent
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Jun 11, 2021
    Copy the full SHA
    32fb8ee View commit details
  2. Merge pull request #609 from mde/dependabot/npm_and_yarn/glob-parent-…

    …5.1.2
    
    Bump glob-parent from 5.1.1 to 5.1.2
    mde authored Jun 11, 2021
    Copy the full SHA
    7b0845d View commit details

Commits on Sep 23, 2021

  1. Updated jsdoc to 3.6.7

    markbrouwer96 committed Sep 23, 2021
    Copy the full SHA
    d5404d6 View commit details

Commits on Sep 25, 2021

  1. Merge pull request #629 from markbrouwer96/main

    Updated jsdoc to 3.6.7
    mde authored Sep 25, 2021
    Copy the full SHA
    e4180b4 View commit details

Commits on Apr 20, 2022

  1. Update packages

    mde committed Apr 20, 2022
    Copy the full SHA
    c028c34 View commit details
  2. Copy the full SHA
    faf8b84 View commit details
  3. Don't use template literal

    mde committed Apr 20, 2022
    4
    Copy the full SHA
    076dcb6 View commit details
  4. Version 3.1.7

    mde committed Apr 20, 2022
    Copy the full SHA
    820855a View commit details
Showing with 4,616 additions and 267 deletions.
  1. +0 −13 docs/syntax.md
  2. +14 −7 jakefile.js
  3. +20 −8 lib/ejs.js
  4. +38 −6 lib/utils.js
  5. +4,204 −198 package-lock.json
  6. +3 −3 package.json
  7. +29 −14 test/cli.js
  8. +57 −18 test/ejs.js
  9. +251 −0 test/utils.js
13 changes: 0 additions & 13 deletions docs/syntax.md
Original file line number Diff line number Diff line change
@@ -21,7 +21,6 @@ Table of contents
- `_%>`: Removes all trailing whitespace
- Literal tags
- Including other files
- “Preprocessor” directive
- JavaScript `include()` function
- Copyright

@@ -439,18 +438,6 @@ template.
The behavior of resolving included file path can be overridden using the
`ejs.resolveInclude` function.

### “Preprocessor” directive

As a compatibility layer with EJS version 1, it is possible to use the
`include` directive in an unescaped output tag to directly “yank” the
text from another file, just like including a C header. However, as it is done
as a simple inclusion, you cannot pass arguments to the included template. You
can however make variables available in the parent template, that will be
visible to the child template as well.

This flavor of `include` is **static**, which means that the resulting
function contains the copy of the included file as when it was compiled, so
if you changed the file after compilation, the changes are not reflected.

#### Whitespace control

21 changes: 14 additions & 7 deletions jakefile.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var fs = require('fs');
var path = require('path');
var execSync = require('child_process').execSync;
var exec = function (cmd) {
execSync(cmd, {stdio: 'inherit'});
@@ -19,45 +20,51 @@ task('clean', ['clobber'], function () {

desc('Lints the source code');
task('lint', ['clean'], function () {
exec('./node_modules/.bin/eslint "**/*.js"');
var epath = path.join('./node_modules/.bin/eslint');
exec(epath+' "**/*.js"');
console.log('Linting completed.');
});

task('browserify', function () {
exec('./node_modules/browserify/bin/cmd.js --standalone ejs lib/ejs.js > ejs.js');
var epath = path.join('./node_modules/browserify/bin/cmd.js');
exec(epath+' --standalone ejs lib/ejs.js > ejs.js');
console.log('Browserification completed.');
});

task('minify', function () {
exec('./node_modules/uglify-js/bin/uglifyjs ejs.js > ejs.min.js');
var epath = path.join('./node_modules/uglify-js/bin/uglifyjs');
exec(epath+' ejs.js > ejs.min.js');
console.log('Minification completed.');
});

desc('Generates the EJS API docs for the public API');
task('doc', function () {
jake.rmRf('out');
exec('./node_modules/.bin/jsdoc --verbose -c jsdoc.json lib/* docs/jsdoc/*');
var epath = path.join('./node_modules/.bin/jsdoc');
exec(epath+' --verbose -c jsdoc.json lib/* docs/jsdoc/*');
console.log('Documentation generated in ./out.');
});

desc('Generates the EJS API docs for the public and private API');
task('devdoc', function () {
jake.rmRf('out');
exec('./node_modules/.bin/jsdoc --verbose -p -c jsdoc.json lib/* docs/jsdoc/*');
var epath = path.join('./node_modules/.bin/jsdoc');
exec(epath+' --verbose -p -c jsdoc.json lib/* docs/jsdoc/*');
console.log('Documentation generated in ./out.');
});

desc('Publishes the EJS API docs');
task('docPublish', ['doc'], function () {
fs.writeFileSync('out/CNAME', 'api.ejs.co');
console.log('Pushing docs to gh-pages...');
exec('./node_modules/.bin/git-directory-deploy --directory out/');
var epath = path.join('./node_modules/.bin/git-directory-deploy');
exec(epath+' --directory out/');
console.log('Docs published to gh-pages.');
});

desc('Runs the EJS test suite');
task('test', ['lint'], function () {
exec('./node_modules/.bin/mocha');
exec(path.join('./node_modules/.bin/mocha'));
});

publishTask('ejs', ['build'], function () {
28 changes: 20 additions & 8 deletions lib/ejs.js
Original file line number Diff line number Diff line change
@@ -44,6 +44,7 @@
* @public
*/


var fs = require('fs');
var path = require('path');
var utils = require('./utils');
@@ -64,6 +65,7 @@ var _OPTS_PASSABLE_WITH_DATA = ['delimiter', 'scope', 'context', 'debug', 'compi
// so we make an exception for `renderFile`
var _OPTS_PASSABLE_WITH_DATA_EXPRESS = _OPTS_PASSABLE_WITH_DATA.concat('cache');
var _BOM = /^\uFEFF/;
var _JS_IDENTIFIER = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;

/**
* EJS template function cache. This can be a LRU object from lru-cache NPM
@@ -305,7 +307,7 @@ function fileLoader(filePath){
*/

function includeFile(path, options) {
var opts = utils.shallowCopy({}, options);
var opts = utils.shallowCopy(utils.createNullProtoObjWherePossible(), options);
opts.filename = getIncludePath(path, opts);
if (typeof options.includer === 'function') {
var includerResult = options.includer(path, opts.filename);
@@ -411,8 +413,8 @@ exports.compile = function compile(template, opts) {
*/

exports.render = function (template, d, o) {
var data = d || {};
var opts = o || {};
var data = d || utils.createNullProtoObjWherePossible();
var opts = o || utils.createNullProtoObjWherePossible();

// No options object -- if there are optiony names
// in the data, copy them to options
@@ -483,7 +485,7 @@ exports.renderFile = function () {
opts.filename = filename;
}
else {
data = {};
data = utils.createNullProtoObjWherePossible();
}

return tryHandleCache(opts, data, cb);
@@ -505,8 +507,8 @@ exports.clearCache = function () {
};

function Template(text, opts) {
opts = opts || {};
var options = {};
opts = opts || utils.createNullProtoObjWherePossible();
var options = utils.createNullProtoObjWherePossible();
this.templateText = text;
/** @type {string | null} */
this.mode = null;
@@ -587,12 +589,21 @@ Template.prototype = {
' var __output = "";\n' +
' function __append(s) { if (s !== undefined && s !== null) __output += s }\n';
if (opts.outputFunctionName) {
if (!_JS_IDENTIFIER.test(opts.outputFunctionName)) {
throw new Error('outputFunctionName is not a valid JS identifier.');
}
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
}
if (opts.localsName && !_JS_IDENTIFIER.test(opts.localsName)) {
throw new Error('localsName is not a valid JS identifier.');
}
if (opts.destructuredLocals && opts.destructuredLocals.length) {
var destructuring = ' var __locals = (' + opts.localsName + ' || {}),\n';
for (var i = 0; i < opts.destructuredLocals.length; i++) {
var name = opts.destructuredLocals[i];
if (!_JS_IDENTIFIER.test(name)) {
throw new Error('destructuredLocals[' + i + '] is not a valid JS identifier.');
}
if (i > 0) {
destructuring += ',\n ';
}
@@ -683,13 +694,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(utils.createNullProtoObjWherePossible(), 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 || utils.createNullProtoObjWherePossible(), escapeFn, include, rethrow]);
};
if (opts.filename && typeof Object.defineProperty === 'function') {
var filename = opts.filename;
44 changes: 38 additions & 6 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -114,8 +114,10 @@ exports.escapeXML.toString = function () {
*/
exports.shallowCopy = function (to, from) {
from = from || {};
for (var p in from) {
to[p] = from[p];
if ((to !== null) && (to !== undefined)) {
for (var p in from) {
to[p] = from[p];
}
}
return to;
};
@@ -133,10 +135,14 @@ exports.shallowCopy = function (to, from) {
* @private
*/
exports.shallowCopyFromList = function (to, from, list) {
for (var i = 0; i < list.length; i++) {
var p = list[i];
if (typeof from[p] != 'undefined') {
to[p] = from[p];
list = list || [];
from = from || {};
if ((to !== null) && (to !== undefined)) {
for (var i = 0; i < list.length; i++) {
var p = list[i];
if (typeof from[p] != 'undefined') {
to[p] = from[p];
}
}
}
return to;
@@ -177,3 +183,29 @@ exports.cache = {
exports.hyphenToCamel = function (str) {
return str.replace(/-[a-z]/g, function (match) { return match[1].toUpperCase(); });
};

/**
* Returns a null-prototype object in runtimes that support it
*
* @return {Object} Object, prototype will be set to null where possible
* @static
* @private
*/
exports.createNullProtoObjWherePossible = (function () {
if (typeof Object.create == 'function') {
return function () {
return Object.create(null);
};
}
if (!({__proto__: null} instanceof Object)) {
return function () {
return {__proto__: null};
};
}
// Not possible, just pass through
return function () {
return {};
};
})();


Loading