From c41ec38b81b42ae84aa2321d517b038b047d165d Mon Sep 17 00:00:00 2001 From: Andrew Sprouse Date: Sat, 29 Jul 2017 13:33:44 -0700 Subject: [PATCH] Support objects created with Object.create(null) by following http://eslint.org/docs/rules/no-prototype-builtins. fixes #468 --- src/compiler.js | 6 +++--- src/environment.js | 2 +- src/filters.js | 2 +- src/lib.js | 2 +- src/runtime.js | 8 ++++++-- tests/compiler.js | 16 ++++++++++++++++ tests/filters.js | 9 +++++++++ tests/runtime.js | 17 +++++++++++++++++ 8 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/compiler.js b/src/compiler.js index 1efa47ce..97b71d0a 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -838,7 +838,7 @@ var Compiler = Object.extend({ 'var callerFrame = frame;', 'frame = ' + ((keepFrame) ? 'frame.push(true);' : 'new runtime.Frame();'), 'kwargs = kwargs || {};', - 'if (kwargs.hasOwnProperty("caller")) {', + 'if (Object.prototype.hasOwnProperty.call(kwargs, "caller")) {', 'frame.set("caller", kwargs.caller); }' ); @@ -856,7 +856,7 @@ var Compiler = Object.extend({ lib.each(kwargs.children, function(pair) { var name = pair.key.value; this.emit('frame.set("' + name + '", ' + - 'kwargs.hasOwnProperty("' + name + '") ? ' + + 'Object.prototype.hasOwnProperty.call(kwargs, "' + name + '") ? ' + 'kwargs["' + name + '"] : '); this._compileExpression(pair.value, frame); this.emitLine(');'); @@ -954,7 +954,7 @@ var Compiler = Object.extend({ alias = name; } - this.emitLine('if(' + importedId + '.hasOwnProperty("' + name + '")) {'); + this.emitLine('if(Object.prototype.hasOwnProperty.call(' + importedId + ', "' + name + '")) {'); this.emitLine('var ' + id + ' = ' + importedId + '.' + name + ';'); this.emitLine('} else {'); this.emitLine('cb(new Error("cannot import \'' + name + '\'")); return;'); diff --git a/src/environment.js b/src/environment.js index e5597a72..7a7ff675 100644 --- a/src/environment.js +++ b/src/environment.js @@ -339,7 +339,7 @@ var Context = Obj.extend({ // Make a duplicate of ctx this.ctx = {}; for(var k in ctx) { - if(ctx.hasOwnProperty(k)) { + if(Object.prototype.hasOwnProperty.call(ctx, k)) { this.ctx[k] = ctx[k]; } } diff --git a/src/filters.js b/src/filters.js index e2afa993..67eaf688 100644 --- a/src/filters.js +++ b/src/filters.js @@ -513,7 +513,7 @@ var filters = { } else { parts = []; for (var k in obj) { - if (obj.hasOwnProperty(k)) { + if (Object.prototype.hasOwnProperty.call(obj, k)) { parts.push(enc(k) + '=' + enc(obj[k])); } } diff --git a/src/lib.js b/src/lib.js index 86bef9ff..1a37a27a 100644 --- a/src/lib.js +++ b/src/lib.js @@ -277,7 +277,7 @@ exports.keys = function(obj) { else { var keys = []; for(var k in obj) { - if(obj.hasOwnProperty(k)) { + if(Object.prototype.hasOwnProperty.call(obj, k)) { keys.push(k); } } diff --git a/src/runtime.js b/src/runtime.js index 5b03d08c..ca2f9eb8 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -126,11 +126,15 @@ function makeKeywordArgs(obj) { return obj; } +function isKeywordArgs(obj) { + return obj && Object.prototype.hasOwnProperty.call(obj, '__keywords'); +} + function getKeywordArgs(args) { var len = args.length; if(len) { var lastArg = args[len - 1]; - if(lastArg && lastArg.hasOwnProperty('__keywords')) { + if(isKeywordArgs(lastArg)) { return lastArg; } } @@ -144,7 +148,7 @@ function numArgs(args) { } var lastArg = args[len - 1]; - if(lastArg && lastArg.hasOwnProperty('__keywords')) { + if(isKeywordArgs(lastArg)) { return len - 1; } else { diff --git a/tests/compiler.js b/tests/compiler.js index 9355d696..d70a3440 100644 --- a/tests/compiler.js +++ b/tests/compiler.js @@ -53,6 +53,22 @@ finish(done); }); + it('should compile references - object without prototype', function(done) { + var context = Object.create(null); + context.foo = Object.create(null); + context.foo.bar = 'baz'; + + equal('{{ foo.bar }}', + context, + 'baz'); + + equal('{{ foo["bar"] }}', + context, + 'baz'); + + finish(done); + }); + it('should fail silently on undefined values', function(done) { equal('{{ foo }}', ''); equal('{{ foo.bar }}', ''); diff --git a/tests/filters.js b/tests/filters.js index ebc73ab0..9bb5b3bf 100644 --- a/tests/filters.js +++ b/tests/filters.js @@ -586,6 +586,15 @@ finish(done); }); + it('urlencode - object without prototype', function(done) { + var obj = Object.create(null); + obj['1'] = 2; + obj['&1'] = '&2'; + + equal('{{ obj | urlencode | safe }}', { obj: obj}, '1=2&%261=%262'); + finish(done); + }); + it('urlize', function(done) { // from jinja test suite: // https://github.com/mitsuhiko/jinja2/blob/8db47916de0e888dd8664b2511e220ab5ecf5c15/jinja2/testsuite/filters.py#L236-L239 diff --git a/tests/runtime.js b/tests/runtime.js index 5aaa6280..0ac462c3 100644 --- a/tests/runtime.js +++ b/tests/runtime.js @@ -76,5 +76,22 @@ finish(done); }); + + it('should allow for objects without a prototype macro arguments in the last position', function(done) { + var noProto = Object.create(null); + noProto.qux = 'world'; + + render('{% macro foo(bar, baz) %}' + + '{{ bar }} {{ baz.qux }}{% endmacro %}' + + '{{ foo("hello", noProto) }}', + {noProto: noProto}, + { noThrow: true }, + function(err, res) { + expect(err).to.equal(null); + expect(res).to.equal('hello world'); + }); + + finish(done); + }); }); })();