Permalink
Browse files

feat #254 Escape for security

  • Loading branch information...
1 parent dcc254c commit e71fe6bba10b50008b1dfa8be7a960c2bbeabd37 @ymeine ymeine committed with Fabio Crisci Nov 29, 2012
@@ -265,7 +265,7 @@ Aria.classDefinition({
if (this._isDebug) {
out.trackLine(statement.lineNumber);
}
- handler.process(out, statement);
+ handler.process(out, statement, this);
}
}
},
@@ -31,7 +31,7 @@
},
"escape" : {
/**
- * Initialisation function called when the template is parsed
+ * Initialization function called when the template is parsed
* @param {aria.templates.ClassWriter} out
*/
init : function (out) {
@@ -47,6 +47,29 @@
return aria.utils.String.escapeHTML(String(s));
}
},
+ "escapeforhtml" : {
+ /**
+ * Initialization function called when the template is parsed
+ * @param {aria.templates.ClassWriter} out
+ */
+ init : function (out) {
+ out.addDependencies(["aria.utils.String"]);
+ },
+ /**
+ * Use the aria.utils.String.escapeForHTML utility to process the given input. If the input is null or
+ * undefined, it is returned as is.
+ * @name aria.templates.Modifiers.escapeForHTML
+ * @param {String} str the input string
+ * @return {String} the processed string
+ * @see aria.utils.String.escapeForHTML
+ */
+ fn : function (s, arg) {
+ if (s == null) {
+ return s;
+ }
+ return aria.utils.String.escapeForHTML(s + '', arg);
+ }
+ },
"capitalize" : {
/**
* Returns the entry in capital letters
@@ -64,10 +87,14 @@
* @name aria.templates.Modifiers.default
* @param {String} str the entry
* @param {String} defaultValue the default value
+ * @param {String} escape the name of the escaping modifier function to use to process the given default
+ * value. When empty, this value is left as is.
* @return {String}
*/
- fn : function (str, defaultValue) {
- return str != null ? str : defaultValue;
+ fn : function (str, defaultValue, escape) {
+ return str != null ? str : escape
+ ? aria.templates.Modifiers.callModifier(escape, [defaultValue])
+ : defaultValue;
}
},
"empty" : {
@@ -76,10 +103,14 @@
* @name aria.templates.Modifiers.empty
* @param {String} str the entry
* @param {String} defaultValue the default value
+ * @param {String} escape the name of the escaping modifier function to use to process the given default
+ * value. When empty, this value is left as is.
* @return {String}
*/
- fn : function (str, defaultValue) {
- return !!str && !/^\s*$/.test(str) ? str : defaultValue;
+ fn : function (str, defaultValue, escape) {
+ return !!str && !/^\s*$/.test(str) ? str : escape
+ ? aria.templates.Modifiers.callModifier(escape, [defaultValue])
+ : defaultValue;
}
},
"pad" : {
@@ -290,7 +321,7 @@
* Template modifiers. Modifiers can be used inside the template syntax
*
* <pre>
- * ${'some text' | modifier}
+ * ${'some text'|modifier}
* </pre>
*
* @singleton
@@ -319,7 +350,6 @@
var modifier = __modifiers[modifierName.toLowerCase()];
if (modifier) {
// call the modifier with this, so that this.$log is available
-
return modifier.fn.apply(this, params);
} else {
this.$logError(aria.templates.Modifiers.UNKNOWN_MODIFIER, [modifierName]);
@@ -339,4 +369,4 @@
}
}
});
-})();
+})();
@@ -87,7 +87,7 @@ Aria.classDefinition({
"#EXPRESSION#" : {
inMacro : true,
container : false,
- process : function (out, statement) {
+ process : function (out, statement, classGenerator) {
var param = statement.paramBlock, nextPipe = utilString.indexOfNotEscaped(param, "|"), parts = [];
// split param against unescaped |
while (nextPipe != -1) {
@@ -96,6 +96,35 @@ Aria.classDefinition({
nextPipe = utilString.indexOfNotEscaped(param, "|");
}
parts.push(param);
+
+ // Check if the secure must be done automatically or not:
+ // - either the modifier is present in the list and thus we leave the default behavior of modifiers
+ // - either we are in the context of a CSS Template and we decide not to escape automatically
+ var automaticSecure = true;
+
+ var escapeModifier = classGenerator.escapeModifier;
+ if (!escapeModifier) {
+ automaticSecure = false;
+ } else {
+ for (var i = parts.length - 1; i >= 1; i--) {
+ var part = parts[i];
+ if (part.indexOf(escapeModifier) === 0) {
+ automaticSecure = false;
+ break;
+ }
+ }
+ }
+
+ /* Begin non backward compatible change */
+ var automaticSecure = false;
+ /* End non backward compatible change */
+
+ // If the secure must be done automatically, we add the modifier at the end of the list, to rely on
+ // this available mechanism
+ if (automaticSecure) {
+ parts.splice(1, 0, escapeModifier);
+ }
+
var beginexpr = [], endexpr = [];
var expr;
var regExp = /^(\w+)(?::([\s\S]*))?$/;
@@ -111,9 +140,11 @@ Aria.classDefinition({
beginexpr.push("this.$modifier('" + modifierName + "',[");
expr = match[2]; // parameters of the modifier
if (expr) {
- endexpr[i] = "," + expr + "])";
- // check the expression
- expr = "[" + expr + "]";
+ endexpr[i] = ", " + expr;
+ if (automaticSecure) {
+ endexpr[i] += ", '" + escapeModifier + "'";
+ }
+ endexpr[i] += "])";
} else {
endexpr[i] = "])";
}
@@ -627,4 +658,4 @@ Aria.classDefinition({
}
};
}
-});
+});
@@ -37,6 +37,12 @@ Aria.classDefinition({
this._classType = "TML";
this._rootStatement = "Library";
this._templateParamBean = "aria.templates.CfgBeans.LibraryCfg";
+
+ /**
+ * Name of the modifier to be used to escape the output for safety
+ * @type String
+ */
+ this.escapeModifier = "escapeForHTML";
},
$prototype : {
$init : function (p) {
@@ -60,4 +66,4 @@ Aria.classDefinition({
this.$ClassGenerator._writeClassInit.call(this, out);
}
}
-});
+});
@@ -37,6 +37,12 @@ Aria.classDefinition({
this._classType = "TPL";
this._rootStatement = "Template";
this._templateParamBean = "aria.templates.CfgBeans.TemplateCfg";
+
+ /**
+ * Name of the modifier to be used to escape the output for safety
+ * @type String
+ */
+ this.escapeModifier = "escapeForHTML";
},
$prototype : {
@@ -96,4 +102,4 @@ Aria.classDefinition({
});
}
}
-});
+});
View
@@ -87,7 +87,7 @@ Aria.classDefinition({
},
/**
- * Escape < and > and & in given string
+ * Escape < and > and & in the given string
* @param {String} str
* @return {String}
*/
@@ -96,6 +96,57 @@ Aria.classDefinition({
},
/**
+ * Escape " and ' the given string.
+ * @param {String} str
+ * @return {String} the escaped string
+ */
+ escapeHTMLAttr : function (str) {
+ return str.replace(/'/g, "&#x27;").replace(/"/g, "&quot;");
+ },
+
+ /**
+ * Escape the given string depending on the options. The string can be escaped for different contexts: - safe
+ * insertion inside an HTML text node - safe insertion inside an attribute value
+ * @param {String} str input string
+ * @param {Object|Boolean} options when it is a boolean, if it is true the input string is escaped for all the
+ * contexts, otherwise it is left unmodified. When it is an object, the string is escaped only for the specified
+ * contexts. Here is the format of the object:
+ *
+ * <pre>
+ * {
+ * attr: true, // will escape the string for safe insertion inside the value of an attribute
+ * text: false // will NOT escape the string for safe insertion inside an HTML text node
+ * }
+ * </pre>
+ *
+ * When the value is nor a boolean neither an object (null, undefined, number...) the string is escaped for all
+ * the contexts (equivalent to passing true).
+ * @return {String} processed string
+ */
+ escapeForHTML : function (str, options) {
+ var escapeForHTMLText = false;
+ var escapeForHTMLAttr = false;
+
+ if (aria.utils.Type.isObject(options)) {
+ escapeForHTMLText = options.text === true;
+ escapeForHTMLAttr = options.attr === true;
+ } else if (!aria.utils.Type.isBoolean(options) || options) {
+ escapeForHTMLText = true;
+ escapeForHTMLAttr = true;
+ }
+
+ if (escapeForHTMLText) {
+ str = this.escapeHTML(str).replace(/\//g, "&#x2F;");
+ }
+
+ if (escapeForHTMLAttr) {
+ str = this.escapeHTMLAttr(str);
+ }
+
+ return str;
+ },
+
+ /**
* Remove accents from a string
* @param {String} stringToStrip the string from which you want to remove accentuation
* @return {String} The string, stripped from any accent
Oops, something went wrong.

0 comments on commit e71fe6b

Please sign in to comment.