Socket
Socket
Sign inDemoInstall

secure-handlebars

Package Overview
Dependencies
184
Maintainers
4
Versions
13
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.1 to 1.2.0

CONTRIBUTORS.md

2

bower.json
{
"name": "secure-handlebars",
"version": "1.1.1",
"version": "1.2.0",
"main": "dist/secure-handlebars.min.js",

@@ -5,0 +5,0 @@ "authors": [

@@ -54,2 +54,6 @@ /*

dest: 'dist/<%= pkg.name %>.min.js'
},
buildMinWithVersion: {
src: ['src/polyfills/*.js', 'dist/<%= pkg.name %>.js'],
dest: 'dist/<%= pkg.name %>.<%= pkg.version %>.min.js'
}

@@ -96,5 +100,23 @@ },

},
bump: {
options: {
files: [ 'package.json', 'bower.json'],
updateConfigs: ['pkg'],
commit: true,
commitMessage: 'Release v%VERSION%',
commitFiles: ['package.json', 'bower.json', 'dist/.'],
createTag: true,
tagName: 'v%VERSION%',
tagMessage: 'Version %VERSION%',
push: false,
pushTo: 'origin',
gitDescribeOptions: '--tags --always --abbrev=1 --dirty=-d',
globalReplace: false,
prereleaseName: false,
regExp: false
}
},
clean: {
all: ['xunit.xml', 'artifacts', 'coverage', 'node_modules'],
buildResidues: ['xunit.xml', 'artifacts', 'coverage']
all: ['artifacts', 'coverage', 'node_modules'],
buildResidues: ['artifacts', 'coverage']
}

@@ -110,2 +132,3 @@ });

grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-bump');

@@ -115,3 +138,4 @@ grunt.registerTask('test', ['clean:buildResidues', 'jshint', 'execute', 'dist', 'karma', 'mocha_istanbul']);

grunt.registerTask('default', ['test']);
grunt.registerTask('release', ['bump-only', 'dist'])
};
};
{
"name": "secure-handlebars",
"version": "1.1.1",
"licenses": [
{
"type": "BSD",
"url": "https://github.com/yahoo/secure-handlebars/blob/master/LICENSE"
}
],
"version": "1.2.0",
"author": "Nera Liu",

@@ -44,23 +38,31 @@ "contributors": [

"dependencies": {
"bluebird": "^2.9.30",
"context-parser": "^2.0.1",
"glob": "^5.0.13",
"handlebars": "^3.0.3",
"html-decoder": "^1.0.2",
"xss-filters": "^1.2.4"
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"xss-filters": "^1.2.6"
},
"devDependencies": {
"bluebird": "^2.9.30",
"chai": "^2.3.0",
"chai": "^3.2.0",
"grunt": "^0.4.5",
"grunt-bower-task": "^0.4.0",
"grunt-browserify": "^3.3.0",
"grunt-browserify": "^4.0.0",
"grunt-bump": "^0.3.2",
"grunt-cli": "^0.1.13",
"grunt-contrib-clean": "^0.6.0",
"grunt-contrib-jshint": "^0.11.0",
"grunt-contrib-uglify": "^0.9.1",
"grunt-contrib-uglify": "^0.9.2",
"grunt-execute": "^0.2.2",
"grunt-karma": "^0.11.0",
"grunt-mocha-istanbul": "^2.3.0",
"grunt-karma": "^0.12.0",
"grunt-mocha-istanbul": "^3.0.1",
"istanbul": "^0.3.18",
"jison": "^0.4.15",
"karma": "^0.12.36",
"karma-mocha": "^0.1.10",
"karma-phantomjs-launcher": "^0.2.0"
"karma": "^0.13.9",
"karma-mocha": "^0.2.0",
"karma-phantomjs-launcher": "^0.2.1",
"mocha": "^2.2.5",
"phantomjs": "^1.9.18"
},

@@ -75,3 +77,3 @@ "main": "./src/secure-handlebars.js",

},
"license": "BSD"
"license": "SEE LICENSE IN LICENSE"
}

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

/*jshint -W030 */
/*jshint -W083 */
(function () {

@@ -27,2 +28,8 @@ "use strict";

// https://github.com/yahoo/secure-handlebars/blob/master/src/handlebars-utils.js#L76
// TODO: double check the case of allowing \/
var rePartialPattern = /^(\{\{~?>\s*)([^\s!"#%&'\(\)\*\+,\.;<=>@\[\\\]\^`\{\|\}\~]+)(.*)/,
// reSJSTPartialSignature = /^SJST\/(?:\d+|SKIP)\//;
reSJSTPartialSignature = /^SJST\/\d+\//;
// extracted from xss-filters

@@ -51,5 +58,5 @@ /*

*/
function ContextParserHandlebarsException(msg, fileName, lineNo, charNo) {
function ContextParserHandlebarsException(msg, filePath, lineNo, charNo) {
this.msg = msg;
this.fileName = fileName;
this.filePath = filePath;
this.lineNo = lineNo;

@@ -69,6 +76,8 @@ this.charNo = charNo;

function ContextParserHandlebars(config) {
config || (config = {});
config.shbsPartialsCache || (config.shbsPartialsCache = {});
/* save the processed char */
this._buffer = [];
/* reset the internal */
this.reset(config.processingFile);

@@ -84,10 +93,39 @@ /* the configuration of ContextParserHandlebars */

/* the flags are used to set to dis/enable partial processing, defaulted to false */
this._config._enablePartialProcessing = config.shbsPartialsCache.raw !== undefined;
/* the flags is used for setting the partial handling */
this._config._enablePartialCombine = (config.enablePartialCombine === true);
/* this flag is used for preventing infinite lookup of partials */
this._config._maxPartialDepth = parseInt(config.maxPartialDepth) || 10;
/* internal file cache */
this._config._rawPartialsCache = config.shbsPartialsCache.raw || {};
/* expose the processed partial cache */
this._config._processedPartialsCache = config.shbsPartialsCache.preprocessed || {};
}
/**
* @function ContextParserHandlebars.reset
*
* @description
* All non-config internal variables are needed to reset!
*/
ContextParserHandlebars.prototype.reset = function(filePath) {
/* save the processed char */
this._buffer = [];
this._partials = [];
/* save the char/line no being processed */
this._charNo = 0;
this._lineNo = 1;
this._fileName = config.processingFile? config.processingFile: '';
this._filePath = filePath || '';
/* context parser for HTML5 parsing */
this.contextParser = parserUtils.getParser();
}
};

@@ -163,7 +201,13 @@ /**

*/
ContextParserHandlebars.prototype.analyzeContext = function(input) {
ContextParserHandlebars.prototype.analyzeContext = function(input, options) {
options || (options = {});
// the last parameter is the hack till we move to LR parser
var ast = this.buildAst(input, 0, []);
var r = this.analyzeAst(ast, this.contextParser, 0);
(this._config._printCharEnable && typeof process === 'object')? process.stdout.write(r.output) : '';
var ast = this.buildAst(input, 0, []),
r = this.analyzeAst(ast, options.contextParser || this.contextParser, 0);
if (this._config._printCharEnable && typeof process === 'object') {
options.disablePrintChar || process.stdout.write(r.output);
}
return r.output;

@@ -339,3 +383,3 @@ };

'[ERROR] SecureHandlebars: ' + exception,
this._fileName,
this._filePath,
this.countNewLineChar(input.slice(0, j)), j);

@@ -390,4 +434,6 @@ handlebarsUtils.handleError(exceptionObj, true);

/*jshint validthis: true */
var j = 0, len = tree.length, node,
re, partialName, enterState, partialContent;
for (var j = 0, len = tree.length, node; j < len; j++) {
for (; j < len; j++) {
node = tree[j];

@@ -403,3 +449,3 @@

// lookupStateForHandlebarsOpenBraceChar from current state before handle it
parser.setCurrentState(ContextParserHandlebars.lookupStateForHandlebarsOpenBraceChar[parser.state]);
parser.state = ContextParserHandlebars.lookupStateForHandlebarsOpenBraceChar[parser.state];
this.clearBuffer();

@@ -416,13 +462,97 @@ this.handleEscapeAndRawTemplate(node.content, 0, parser);

// TODO: we support basic partial only, need to enhance it.
// http://handlebarsjs.com/partials.html
} else if (node.type === handlebarsUtils.PARTIAL_EXPRESSION &&
(re = handlebarsUtils.isValidExpression(node.content, 0, node.type)) &&
(partialName = re.tag)) {
// if the partialName is generated by us, there is no need to reprocess it again,
// just put back the partial expression is fine
if (reSJSTPartialSignature.test(partialName)) {
output += node.content;
continue;
}
partialContent = this._config._rawPartialsCache[partialName];
if (this._config._enablePartialProcessing && typeof partialContent === 'string') {
this._partials.push(node.content);
if (this._partials.length >= this._config._maxPartialDepth) {
msg = "[ERROR] SecureHandlebars: The partial inclusion chain (";
msg += this._partials.join(' > ') + ") has exceeded the maximum number of allowed depths (maxPartialDepth: "+this._config._maxPartialDepth+").";
msg += "\nPlease follow this URL to resolve - https://github.com/yahoo/secure-handlebars#warnings-and-workarounds";
exceptionObj = new ContextParserHandlebarsException(msg, this._filePath, this._lineNo, this._charNo);
handlebarsUtils.handleError(exceptionObj, true);
}
// get the html state number right the parital is called, and analyzed
enterState = parser.getCurrentState();
// TODO: this._filePath now does not reflect an error that occurs inside a partial, need to enhance it later
// while analyzing the partial, use the current parser (possibly forked) and disable printChar
partialContent = this.analyzeContext(partialContent, {
contextParser: parser,
disablePrintChar: true
});
if (this._config._enablePartialCombine) {
output += partialContent;
} else {
partialName = 'SJST/' + enterState + '/' + partialName;
// rewrite the partial name, that is prefixed with the in-state
output += node.content.replace(rePartialPattern, function(m, p1, p2, p3) {
return p1 + partialName + p3;
});
this._config._processedPartialsCache[partialName] = partialContent;
}
this._partials.pop();
} else {
output += node.content; // this._config._strictMode=true will throw
msg = (this._config._strictMode? '[ERROR]' : '[WARNING]') + " SecureHandlebars: ";
if (this._config._enablePartialProcessing) {
msg += (partialContent === undefined) ?
"Failed to load the partial content of " :
"Failed to perform contextual analysis over (pre-)compiled partial ";
} else {
// No matter a partial expression is placed in a data state, we don't know whether the partial content end in DATA state, so warn the user
// if (parser.getCurrentState() !== stateMachine.State.STATE_DATA) {
// msg += node.content + ' is placed in a non-text context.';
// }
msg += 'Please enable contextual analysis over the partial content of ';
}
msg += node.content;
msg += "\nPlease follow this URL to resolve - https://github.com/yahoo/secure-handlebars#warnings-and-workarounds";
exceptionObj = new ContextParserHandlebarsException(msg, this._filePath, this._lineNo, this._charNo);
handlebarsUtils.handleError(exceptionObj, this._config._strictMode);
}
// TODO: content inside RAW_BLOCK should be analysed too
} else if (node.type === handlebarsUtils.RAW_BLOCK ||
node.type === handlebarsUtils.PARTIAL_EXPRESSION ||
node.type === handlebarsUtils.AMPERSAND_EXPRESSION) {
// if the 'rawblock', 'partial expression' and 'ampersand expression' are not in Data State,
// if the 'rawblock' and 'ampersand expression' are not in Data State,
// we should warn the developers or throw exception in strict mode
if (parser.getCurrentState() !== stateMachine.State.STATE_DATA) {
msg = (this._config._strictMode? '[ERROR]' : '[WARNING]') + " SecureHandlebars: " + node.content + ' is in non-HTML Context!';
exceptionObj = new ContextParserHandlebarsException(msg, this._fileName, this._lineNo, this._charNo);
msg = (this._config._strictMode? '[ERROR]' : '[WARNING]') + " SecureHandlebars: " + node.content + ' is placed in a non-text context!';
msg += "\nPlease follow this URL to resolve - https://github.com/yahoo/secure-handlebars#warnings-and-workarounds";
exceptionObj = new ContextParserHandlebarsException(msg, this._filePath, this._lineNo, this._charNo);
handlebarsUtils.handleError(exceptionObj, this._config._strictMode);
}
output += node.content;

@@ -465,3 +595,3 @@ } else if (node.type === handlebarsUtils.BRANCH_EXPRESSION ||

msg += "attributeNameType:("+leftParser.getAttributeNameType()+"/"+rightParser.getAttributeNameType()+")";
exceptionObj = new ContextParserHandlebarsException(msg, this._fileName, this._lineNo, this._charNo);
exceptionObj = new ContextParserHandlebarsException(msg, this._filePath, this._lineNo, this._charNo);
handlebarsUtils.handleError(exceptionObj, true);

@@ -664,3 +794,3 @@ }

exceptionObj = new ContextParserHandlebarsException(errorMessage, this._fileName, this._lineNo, this._charNo);
exceptionObj = new ContextParserHandlebarsException(errorMessage, this._filePath, this._lineNo, this._charNo);
handlebarsUtils.handleError(exceptionObj, this._config._strictMode);

@@ -767,3 +897,3 @@ } else {

'[ERROR] SecureHandlebars: ' + exception,
this._fileName,
this._filePath,
this._lineNo,

@@ -860,4 +990,4 @@ this._charNo);

}
msg = "[ERROR] SecureHandlebars: Parsing error! Cannot encounter '}}' close brace of escape expression.";
exceptionObj = new ContextParserHandlebarsException(msg, this._fileName, this._lineNo, this._charNo);
msg = "[ERROR] SecureHandlebars: Parse error! Cannot encounter '}}' close brace of escape expression.";
exceptionObj = new ContextParserHandlebarsException(msg, this._filePath, this._lineNo, this._charNo);
handlebarsUtils.handleError(exceptionObj, true);

@@ -864,0 +994,0 @@ };

@@ -75,4 +75,5 @@ /*

/* '{{' '~'? '>' '\s'* ('not \s, special-char'+) '\s'* 'not ~{}'* non-greedy '}}' and not follow by '}' */
/* slash should be allowed */
HandlebarsUtils.PARTIAL_EXPRESSION = 3; // {{>.*}}
HandlebarsUtils.partialExpressionRegExp = /^\{\{~?>\s*([^\s!"#%&'\(\)\*\+,\.\/;<=>@\[\\\]\^`\{\|\}\~]+)\s*[^~\}\{]*~?\}\}(?!})/;
HandlebarsUtils.partialExpressionRegExp = /^\{\{~?>\s*([^\s!"#%&'\(\)\*\+,\.;<=>@\[\\\]\^`\{\|\}\~]+)\s*([^~\}\{]*)~?\}\}(?!})/;

@@ -298,3 +299,3 @@ /* '{{' '~'? '# or ^' '\s'* ('not \s, special-char'+) '\s'* 'not {}~'* '~'? non-greedy '}}' and not follow by '}' */

HandlebarsUtils.handleError = function(exceptionObj, throwErr) {
HandlebarsUtils.warn(exceptionObj.msg + (exceptionObj.fileName !== ''? '\n'+exceptionObj.fileName:'') + " [lineNo:" + exceptionObj.lineNo + ",charNo:" + exceptionObj.charNo + "]");
HandlebarsUtils.warn(exceptionObj.msg + (exceptionObj.filePath !== ''? '\n'+exceptionObj.filePath:'') + " [lineNo:" + exceptionObj.lineNo + ",charNo:" + exceptionObj.charNo + "]");
if (throwErr) {

@@ -337,5 +338,4 @@ throw exceptionObj;

module.exports = HandlebarsUtils;
})();

@@ -110,3 +110,3 @@ /*

Parser.prototype.cloneStates = function(parser) {
this.state = parser.getLastState();
this.state = parser.getCurrentState();
this.attrName = parser.getAttributeName();

@@ -128,3 +128,3 @@ this.attributeValue = parser.getAttributeValue();

enableVoidingIEConditionalComments: true,
enableStateTracking: true
enableStateTracking: false
});

@@ -131,0 +131,0 @@ };

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

options = options || {};
options.printCharEnable = false;
var k, parser;

@@ -46,5 +48,3 @@

if (template) {
parser = new ContextParserHandlebars({ printCharEnable: false,
processingFile: options.processingFile,
strictMode: options.strictMode});
parser = new ContextParserHandlebars(options);
return parser.analyzeContext(template);

@@ -73,3 +73,4 @@ }

// expose the original compile
// expose the original (pre-)/compile
h.precompilePreprocessed = pc;
h.compilePreprocessed = c;

@@ -76,0 +77,0 @@

@@ -93,6 +93,23 @@ /*

expect(output).to.be.equal('<!--hello--><a href=x-javascript:alert(1) style="background:url(##javascript:alert\\28 1\\29 )" id="12`&quot;\'3" id=\'12`"&#39;3\'>hello</a>');
expect(output).to.be.equal('<!--hello--><a href=x-javascript:alert(1) style="background:url(##javascript:alert%281%29)" id="12`&quot;\'3" id=\'12`"&#39;3\'>hello</a>');
});
});
describe("SecureHandlebars: preprocess tests", function() {
it('smoke template', function(){
var html = '<!--{{hello}}--><a href={{url}} style="background:url({{url}})" id="{{id}}" id=\'{{id}}\'>{{hello}}</a>';
var data = {
'id': '12`"\'3',
'hello': 'hello',
'url': 'javascript:alert(1)'
};
var preprocessed = Handlebars.preprocess(html);
expect(preprocessed).to.be.equal('<!--{{{yc hello}}}--><a href={{{yubl (yavu (yufull url))}}} style="background:url({{{yubl (yavd (yceuu url))}}})" id="{{{yavd id}}}" id=\'{{{yavs id}}}\'>{{{yd hello}}}</a>');
var output = Handlebars.compilePreprocessed(preprocessed)(data);
expect(output).to.be.equal('<!--hello--><a href=x-javascript:alert(1) style="background:url(##javascript:alert%281%29)" id="12`&quot;\'3" id=\'12`"&#39;3\'>hello</a>');
});
});
describe("SecureHandlebars: compilation error tests", function() {

@@ -99,0 +116,0 @@ it('fallback template', function(){

@@ -18,5 +18,7 @@ /*

expect = require('chai').expect,
ContextParserHandlebars = require("../../src/context-parser-handlebars");
ContextParserHandlebars = require("../../src/context-parser-handlebars"),
exec = promise.promisify(require("child_process").exec);
var config = {};
// TODO: need to improve the processing time of the unit test.
var config = {}, t = 500;
config.printCharEnable = false;

@@ -42,5 +44,5 @@

it(testObj.title, function(done) {
var exec = promise.promisify(require("child_process").exec);
exec('./bin/handlebarspp '+testObj.file)
.timeout(300)
.timeout(t)
.done(function(e){

@@ -58,5 +60,5 @@ testObj.result.forEach(function(r) {

it(testObj.title, function(done) {
var exec = promise.promisify(require("child_process").exec);
exec('./bin/handlebarspp '+testObj.file)
.timeout(300)
exec('./bin/handlebarspp '+testObj.file+' -e .hbs -p tests/samples/files/partials')
.timeout(t)
.done(function(e){

@@ -71,8 +73,30 @@ testObj.result.forEach(function(r) {

/* partial tests */
testPatterns.partialPatterns.forEach(function(testObj) {
it(testObj.title, function(done) {
var params = ' -e .hbs';
if (testObj.partialProcessing) {
params += ' -p tests/samples/files/partials';
}
if (testObj.combine) {
params += ' -c';
}
exec('./bin/handlebarspp '+ testObj.file + params)
.timeout(t)
.done(function(e){
testObj.result.forEach(function(r) {
expect(e.toString()).to.match(r);
});
done();
});
});
});
/* exception tests */
testPatterns.exceptionPatterns.forEach(function(testObj) {
it(testObj.title, function(done) {
var exec = promise.promisify(require("child_process").exec);
exec('./bin/handlebarspp '+testObj.file+' '+testObj.strictMode)
.timeout(300)
exec('./bin/handlebarspp '+testObj.file+' -e .hbs -p tests/samples/files/partials' + (testObj.strictMode ? ' -s' : ''))
.timeout(t)
.catch(function(e){

@@ -90,5 +114,5 @@ testObj.result.forEach(function(r) {

it(testObj.title, function(done) {
var exec = promise.promisify(require("child_process").exec);
exec('./bin/handlebarspp '+testObj.file)
.timeout(300)
.timeout(t)
.done(function(e){

@@ -95,0 +119,0 @@ testObj.result.forEach(function(r) {

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

expect(secureHandlebars.compile('')(data)).to.be.equal('');
expect(secureHandlebars.preprocess('')).to.be.equal('');
});

@@ -72,2 +73,5 @@

var templatePreProcessed = secureHandlebars.preprocess(template);
expect(templatePreProcessed).to.be.equal('<a href="{{{yubl (yavd (yufull url))}}}">hello</a>');
var t1 = secureHandlebars.compilePreprocessed(templatePreProcessed);

@@ -74,0 +78,0 @@

@@ -83,3 +83,3 @@ /*

parser2.cloneStates(parser1);
expect(parser2.getLastState()).to.equal(1);
expect(parser2.getCurrentState()).to.equal(1);
expect(parser2.getAttributeName()).to.equal("href");

@@ -86,0 +86,0 @@ expect(parser2.getAttributeValue()).to.equal("http://www.abc.com");

@@ -188,4 +188,3 @@ /*

});
});
}());

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

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