Socket
Socket
Sign inDemoInstall

express-secure-handlebars

Package Overview
Dependencies
194
Maintainers
5
Versions
15
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.4 to 2.1.0

tests/express/routes/looppartial.js

6

package.json
{
"name": "express-secure-handlebars",
"version": "2.0.4",
"version": "2.1.0",
"licenses": [

@@ -41,2 +41,4 @@ {

"handlebars": "^3.0.3",
"object.assign": "^3.0.1",
"promise": "^7.0.3",
"secure-handlebars": "^1.1.1",

@@ -53,2 +55,4 @@ "xss-filters": "^1.2.4"

"grunt-mocha-istanbul": "^2.3.0",
"mocha": "^2.3.4",
"istanbul": "^0.4.0",
"supertest": "^0.15.0"

@@ -55,0 +59,0 @@ },

@@ -12,4 +12,7 @@ /*

var util = require("util"),
expressHandlebars = require('express-handlebars').ExpressHandlebars,
secureHandlebars = require('secure-handlebars');
ExpressHandlebars = require('express-handlebars').ExpressHandlebars,
secureHandlebars = require('secure-handlebars'),
assign = Object.assign || require('object.assign'),
Promise = require('promise'),
path = require('path');

@@ -24,16 +27,119 @@ function ExpressSecureHandlebars(config) {

this.constructor.super_.call(this, config);
// dedicate a special express-handlebars for fetching raw partials
this._exphbsForRawPartials = new ExpressHandlebarsForRawPartials(config);
this._partialsCache = {
preprocessed: {}
};
// compilerOptions is being used for passing params to secure-handlebars
this.compilerOptions || (this.compilerOptions = {});
this.compilerOptions.shbsPartialsCache = this._partialsCache;
}
/* inheriting the express-handlebars */
util.inherits(ExpressSecureHandlebars, expressHandlebars);
util.inherits(ExpressSecureHandlebars, ExpressHandlebars);
/* override ExpressHandlebars.render() to expose filePath as compilerOptions */
// always returns an empty cache, since the partial is loaded and analyzed on-demand
// TODO: preprocess all templates, and export all partials for compatibility
ExpressSecureHandlebars.prototype.getPartials = function (options) {
return {};
};
// retrieve file content directly from the preprocessed partial cache if it exists
ExpressSecureHandlebars.prototype._getFile = function (filePath, options) {
var file = this._partialsCache.preprocessed[path.relative('.', filePath)];
return file ? Promise.resolve(file) : ExpressHandlebars.prototype._getFile.call(this, filePath, options);
};
// _getTemplates literally has the core of getTemplates, except that it takes an array of filePaths as input
/*
// getTemplates can be rewritten to use _getTemplates in the following way
ExpressSecureHandlebars.prototype.getTemplates = function (dirPath, options) {
options || (options = {});
return this._getDir(dirPath, {cache: options.cache}).map(function (filePath) {
return path.join(dirPath, filePath);
}).then(function(filePaths) {
return this._getTemplates(filePaths, options);
}.bind(this));
};
*/
ExpressSecureHandlebars.prototype._getTemplates = function (filePaths, options) {
options || (options = {});
var templates = filePaths.map(function (filePath) {
return this.getTemplate(filePath, options);
}, this);
return Promise.all(templates).then(function (templates) {
return filePaths.reduce(function (hash, filePath, i) {
hash[filePath] = templates[i];
return hash;
}, {});
});
};
// override render() for on-demand partial preprocessing and (pre-)compilation
ExpressSecureHandlebars.prototype.render = function (filePath, context, options) {
options || (options = {});
// expose filePath as processingFile in compilerOptions for secure-handlebars
this.compilerOptions || (this.compilerOptions = {});
this.compilerOptions.processingFile = filePath;
return expressHandlebars.prototype.render.call(this, filePath, context, options);
// ensure getPartials() is called to fetch raw partials
return this._exphbsForRawPartials.getPartials(options).then(function(rawPartialsCache) {
// in constructor, this.compilerOptions.shbsPartialsCache = this._partialsCache
this._partialsCache.raw = rawPartialsCache;
// this._partialsCache.preprocessed is filled by the underlying (pre-)compile() in getTemplate()
// TODO: improve this by keeping the returned (pre-)compiled template
return this.getTemplate(filePath, options).catch(function(err){
throw err;
});
}.bind(this)).then(function() {
var partialKeys = Object.keys(this._partialsCache.preprocessed);
// disable preprocessing partials in partial
this.compilerOptions.enablePartialProcessing = false;
// Merge render-level and (pre-)compiled pre-processed partials together
return Promise.all([
this._getTemplates(partialKeys, options), // (pre-)compile pre-processed partials
options.partials // collected at renderView
]).then(function (partials) {
return assign.apply(null, [{}].concat(partials));
}).catch(function(err) {
throw err;
}).finally(function() {
// re-enable partial handling
this.compilerOptions.enablePartialProcessing = true;
}.bind(this));
}.bind(this)).then(function(partials) {
options.partials = partials;
return ExpressHandlebars.prototype.render.call(this, filePath, context, options);
}.bind(this));
};
function ExpressHandlebarsForRawPartials(config) {
this.constructor.super_.call(this, config);
}
util.inherits(ExpressHandlebarsForRawPartials, ExpressHandlebars);
ExpressHandlebarsForRawPartials.prototype._precompileTemplate = ExpressHandlebarsForRawPartials.prototype._compileTemplate = function (template, options) {
return template;
};
/* exporting the same signature of express-handlebars */

@@ -40,0 +146,0 @@ exports = module.exports = exphbs;

@@ -8,4 +8,10 @@ var express = require('express'),

var routesyd = require('./routes/yd');
var routesundefined = require('./routes/undefined');
var routespartial = require('./routes/partial');
var routeslooppartial = require('./routes/looppartial');
app.engine('hbs', expressHbs({ extname:'hbs' }));
app.expressSecureHandlebars = expressHbs.create({ partialsDir: __dirname + '/views/partials',
extname:'hbs' });
app.engine('hbs', app.expressSecureHandlebars.engine);
app.set('views', path.join(__dirname, 'views'));

@@ -18,2 +24,5 @@ app.set('view engine', 'hbs');

app.use('/yd', routesyd);
app.use('/undefined', routesundefined);
app.use('/partial', routespartial);
app.use('/looppartial', routeslooppartial);
app.get('/ok', function(req, res){

@@ -20,0 +29,0 @@ res.status(200).send('ok');

@@ -26,3 +26,2 @@ /*

.get('/ok')
.expect(200)
.end(function(err, res) {

@@ -38,3 +37,2 @@ expect(res.text).to.be.equal('ok');

.get('/')
.expect(200)
.end(function(err, res) {

@@ -50,3 +48,2 @@ expect(res.text).to.be.equal('<h1>express secure handlebars</h1>\n');

.get('/yd')
.expect(200)
.end(function(err, res) {

@@ -59,4 +56,59 @@ expect(res.text).to.be.equal("<div>>&lt;'\"& </div>\n");

it("Express Secure Handlebars undefined data binding test", function(done) {
request(app)
.get('/undefined')
.end(function(err, res) {
expect(res.text).to.be.match(/<div><\/div>/);
expect(res.text).to.be.match(/<input id=\ufffd>/);
expect(res.status).to.be.equal(200);
done();
});
});
it("Express Secure Handlebars partial test", function(done) {
request(app)
.get('/partial')
.end(function(err, res) {
expect(res.text).to.be.match(/header/);
expect(res.text).to.be.match(/js/);
expect(res.status).to.be.equal(200);
done();
});
});
it("Express Secure Handlebars partial cache test", function(done) {
var cacheTest = function() {
var esh = app.expressSecureHandlebars;
expect(esh.compilerOptions).to.be.ok();
expect(esh.compilerOptions.shbsPartialsCache).to.be.ok();
expect(esh.compilerOptions.shbsPartialsCache.raw['header']).to.equal('{{exp}}\n\n');
expect(esh.compilerOptions.shbsPartialsCache.raw['l1']).to.equal('{{> l2 }}\n');
expect(esh.compilerOptions.shbsPartialsCache.raw['l2']).to.equal('{{> l1 }}\n');
expect(esh.compilerOptions.shbsPartialsCache.raw['script']).to.equal('{{js}}\n');
expect(esh.compilerOptions.shbsPartialsCache.preprocessed['SJST/1/header']).to.equal('{{{yd exp}}}\n\n');
expect(esh.compilerOptions.shbsPartialsCache.preprocessed['SJST/6/script']).to.equal('{{{y js}}}\n');
// TODO: compiled is available only when options.cache is true
// expect(esh.compilerOptions.shbsPartialsCache.compiled['SJST/1/header']).to.be.ok();
// expect(esh.compilerOptions.shbsPartialsCache.compiled['SJST/6/script']).to.be.ok();
done();
}
request(app)
.get('/partial')
.end(function(err, res) {
cacheTest();
});
});
// make sure it won't be infinite loop
it("Express Secure Handlebars loop partial test", function(done) {
request(app)
.get('/looppartial')
.end(function(err, res) {
expect(res.status).to.be.equal(500);
done();
});
});
});
}());

25

tests/unit/run-express-secure-handlebars.js

@@ -21,2 +21,4 @@ /*

this.timeout(5000);
it("same signature test", function() {

@@ -30,3 +32,2 @@ // console.log(expressHandlebars);

// console.log(expressSecureHandlebars);

@@ -85,12 +86,22 @@ expect(typeof expressSecureHandlebars).to.be.equal('function');

it("handlebars getTemplate test", function() {
var templateFile = path.resolve("views/yd.hbs");
it("handlebars render test", function(done) {
var filePath = path.resolve('../express/views/yd.hbs');
var expSecureHbs = expressSecureHandlebars.create();
expSecureHbs.render(templateFile);
expect(expSecureHbs.compilerOptions).to.be.ok();
expect(expSecureHbs.compilerOptions.processingFile).to.be.ok();
expect(expSecureHbs.compilerOptions.processingFile).to.be.match(/yd\.hbs/);
expect(expSecureHbs.compilerOptions.shbsPartialsCache).to.be.ok();
expSecureHbs.render(filePath, {input: '<script>alert(1)</script>'}).then(function(output){
if (output === '<div>&lt;script>alert(1)&lt;/script></div>\n' &&
expSecureHbs.compilerOptions.processingFile === filePath) {
done();
}
});
});
});
}());
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc