Skip to content
This repository was archived by the owner on Sep 5, 2018. It is now read-only.

Commit 80b1590

Browse files
committed
Merge pull request #55 from neraliu/strict-context-parser-integration
strict context parser and strict css parser integration
2 parents 54f65c2 + 2fd07ab commit 80b1590

16 files changed

+421
-1471
lines changed

Gruntfile.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module.exports = function(grunt) {
1010
execute: {
1111
cssparser: {
1212
options: {
13-
args: ['src/css-parser/css-parser.21.attr.partial.y', 'src/css-parser/css.21.l', '--outfile', 'src/css-parser/css-parser.js']
13+
args: ['src/css-parser/css-parser.strict.attr.partial.y', 'src/css-parser/css.strict.l', '--outfile', 'src/css-parser/css-parser.js']
1414
},
1515
src: ['node_modules/jison/lib/cli.js']
1616
},
@@ -81,6 +81,9 @@ module.exports = function(grunt) {
8181
options: {
8282
excludes: [
8383
'src/css-parser/css-parser.js',
84+
'src/html-decoder/*.js',
85+
'src/html-decoder/gen/*.js',
86+
'src/html-decoder/polyfills/*.js',
8487
'src/polyfills/browser.js',
8588
'src/polyfills/minimal.js'
8689
],

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "secure-handlebars",
3-
"version": "0.3.0",
3+
"version": "1.0.0",
44
"licenses": [
55
{
66
"type": "BSD",
@@ -41,9 +41,8 @@
4141
"test": "grunt test"
4242
},
4343
"dependencies": {
44-
"context-parser": "^1.0.0",
45-
"debug": "^2.1.2",
46-
"xss-filters": "^1.1.0"
44+
"context-parser": "^1.1.0",
45+
"xss-filters": "^1.2.0"
4746
},
4847
"devDependencies": {
4948
"bluebird": "^2.9.21",
@@ -56,8 +55,9 @@
5655
"grunt-contrib-uglify": "^0.9.1",
5756
"grunt-execute": "^0.2.2",
5857
"grunt-karma": "^0.11.0",
59-
"karma-mocha": "~0.1.10",
6058
"grunt-mocha-istanbul": "^2.3.0",
59+
"karma": "^0.12.36",
60+
"karma-mocha": "~0.1.10",
6161
"karma-phantomjs-launcher": "^0.2.0",
6262
"handlebars": "^3.0.1"
6363
},

src/context-parser-handlebars.js

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ Authors: Nera Liu <neraliu@yahoo-inc.com>
1111
(function () {
1212
"use strict";
1313

14-
/* debug facility */
15-
var debug = require('debug')('cph');
16-
1714
/* import the required package */
1815
var ContextParser = require('./strict-context-parser.js'),
16+
configContextParser = {
17+
enableInputPreProcessing: true,
18+
enableCanonicalization: true,
19+
enableIEConditionalComments: true,
20+
enableStateTracking: true
21+
},
1922
handlebarsUtils = require('./handlebars-utils.js'),
2023
stateMachine = require('context-parser').StateMachine;
2124

@@ -107,9 +110,8 @@ function ContextParserHandlebars(config) {
107110
this._charNo = 0;
108111
this._lineNo = 1;
109112

110-
// TODO: enforce the strict CP by overridding the config object.
111113
/* context parser for HTML5 parsing */
112-
this.contextParser = new ContextParser(config);
114+
this.contextParser = new ContextParser(configContextParser);
113115
}
114116

115117
/**
@@ -400,7 +402,7 @@ ContextParserHandlebars.prototype.generateNodeObject = function(type, content, s
400402
ContextParserHandlebars.prototype.analyzeAst = function(ast, contextParser, charNo) {
401403

402404
var output = '', leftParser, rightParser,
403-
t, msg, exceptionObj, debugString = [];
405+
t, msg, exceptionObj;
404406

405407
this._charNo = charNo;
406408

@@ -412,7 +414,7 @@ ContextParserHandlebars.prototype.analyzeAst = function(ast, contextParser, char
412414

413415
if (node.type === 'html') {
414416

415-
output += parser.parsePartial(node.content);
417+
output += parser.contextualize(node.content);
416418

417419
} else if (node.type === 'escapeexpression' ||
418420
node.type === 'rawexpression') {
@@ -426,10 +428,7 @@ ContextParserHandlebars.prototype.analyzeAst = function(ast, contextParser, char
426428
} else if (node.type === 'node') {
427429

428430
t = this.analyzeAst(node.content, parser, node.startPos);
429-
// cloning states from the branches
430-
parser.state = t.parser.state;
431-
parser.attributeName = t.parser.attributeName;
432-
parser.attributeValue = t.parser.attributeValue;
431+
parser.cloneStates(t.parser);
433432

434433
output += t.output;
435434

@@ -467,7 +466,6 @@ ContextParserHandlebars.prototype.analyzeAst = function(ast, contextParser, char
467466
leftParser.getAttributeNameType() !== rightParser.getAttributeNameType())
468467
)
469468
) {
470-
// debug("analyzeAst:["+r.parsers[0].state+"/"+r.parsers[1].state+"]");
471469
msg = "[ERROR] SecureHandlebars: Inconsistent HTML5 state after conditional branches. Please fix your template! ";
472470
msg += "state:("+leftParser.state+"/"+rightParser.state+"),";
473471
msg += "attributeNameType:("+leftParser.getAttributeNameType()+"/"+rightParser.getAttributeNameType()+")";
@@ -510,7 +508,6 @@ ContextParserHandlebars.prototype.handleTemplate = function(input, i, stateObj)
510508
if (input[i] === '{' && i+2<len && input[i+1] === '{' && input[i+2] === '{') {
511509
handlebarsExpressionType = handlebarsUtils.RAW_EXPRESSION;
512510
/* _handleRawExpression and no validation need, it is safe guard in buildAst function */
513-
debug("handleTemplate:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i+",state:"+stateObj.state);
514511
obj = this.consumeExpression(input, i, handlebarsExpressionType, true);
515512
return;
516513
} else if (input[i] === '{' && i+1<len && input[i+1] === '{') {
@@ -519,7 +516,6 @@ ContextParserHandlebars.prototype.handleTemplate = function(input, i, stateObj)
519516
switch (handlebarsExpressionType) {
520517
case handlebarsUtils.ESCAPE_EXPRESSION:
521518
/* handleEscapeExpression and no validation need, it is safe guard in buildAst function */
522-
debug("handleTemplate:handlebarsExpressionType:"+handlebarsExpressionType,",i:"+i+",state:"+stateObj.state);
523519
obj = this.handleEscapeExpression(input, i, len, stateObj, true);
524520
return;
525521
default:
@@ -553,8 +549,8 @@ ContextParserHandlebars.prototype.addFilters = function(parser, input) {
553549
var isFullUri = false, filters = [], f, exceptionObj, errorMessage,
554550
state = parser.state,
555551
tagName = parser.getStartTagName(),
556-
attributeName = parser.attributeName,
557-
attributeValue = parser.attributeValue;
552+
attributeName = parser.getAttributeName(),
553+
attributeValue = parser.getAttributeValue();
558554

559555
try {
560556

@@ -594,9 +590,13 @@ ContextParserHandlebars.prototype.addFilters = function(parser, input) {
594590
filters.push(f);
595591

596592
} else if (parser.getAttributeNameType() === ContextParser.ATTRTYPE_CSS) { // CSS
597-
598-
attributeValue = htmlDecoder.decode(attributeValue);
599-
var r = cssParserUtils.parseStyleAttributeValue(attributeValue);
593+
var r;
594+
try {
595+
attributeValue = htmlDecoder.decode(attributeValue);
596+
r = cssParserUtils.parseStyleAttributeValue(attributeValue);
597+
} catch (e) {
598+
throw 'Unsafe output expression @ attribute style CSS context (Parsing error OR expression position not supported!)';
599+
}
600600
switch(r.code) {
601601
case cssParserUtils.STYLE_ATTRIBUTE_URL_UNQUOTED:
602602
filters.push(filter.FILTER_ATTRIBUTE_VALUE_STYLE_EXPR_URL_UNQUOTED);
@@ -694,11 +694,10 @@ ContextParserHandlebars.prototype.addFilters = function(parser, input) {
694694
// TODO: need tagname tracing in Context Parser such that we can have
695695
// ability to capture the case of putting output expression within dangerous tag.
696696
// like svg etc.
697-
// the following will be caught by parser.isScriptableTag() anyway
698-
// case stateMachine.State.STATE_SCRIPT_DATA: // 6
699-
// throw 'inside <script> tag (i.e., SCRIPT_DATA state)';
697+
// the following will be caught by handlebarsUtils.isScriptableTag(tagName) anyway
698+
case stateMachine.State.STATE_SCRIPT_DATA: // 6
699+
throw 'inside <script> tag (i.e., SCRIPT_DATA state)';
700700

701-
702701
// should not fall into the following states
703702
case stateMachine.State.STATE_BEFORE_ATTRIBUTE_VALUE: // 37
704703
throw 'unexpectedly BEFORE_ATTRIBUTE_VALUE state';
@@ -715,7 +714,7 @@ ContextParserHandlebars.prototype.addFilters = function(parser, input) {
715714
// To be secure, scriptable tags when encountered will anyway throw an error/warning
716715
// they require either special parsers of their own context (e.g., CSS/script parsers)
717716
// or an application-specific whitelisted url check (e.g., <script src=""> with yubl-yavu-yufull is not enough)
718-
errorMessage += parser.isScriptableTag() ? 'scriptable <' + tagName + '> tag' : exception;
717+
errorMessage += handlebarsUtils.isScriptableTag(tagName) ? 'scriptable <' + tagName + '> tag' : exception;
719718

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

src/css-parser/css-parser.21.attr.partial.y renamed to src/css-parser/css-parser.strict.attr.partial.y

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ See the accompanying LICENSE file for terms.
55
66
Authors: Nera Liu <neraliu@yahoo-inc.com, neraliu@gmail.com>
77
8-
Grammer of CSS 2.1 specification
9-
108
Reference:
119
- http://www.w3.org/TR/2011/REC-CSS2-20110607/syndata.html
1210
- http://www.w3.org/TR/2011/REC-CSS2-20110607/grammar.html

src/css-parser/css.21.l

Lines changed: 0 additions & 143 deletions
This file was deleted.

0 commit comments

Comments
 (0)