diff --git a/app/assets/javascripts/openproject.js b/app/assets/javascripts/openproject.js
index 5a37bb348c5e..89bad5580552 100644
--- a/app/assets/javascripts/openproject.js
+++ b/app/assets/javascripts/openproject.js
@@ -115,6 +115,24 @@ window.OpenProject = (function ($) {
return (str+'').replace(REGEXP_ESCAPE, "\\$1");
};
+ /**
+ * Use select2's escapeMarkup function for correctly escaping
+ * text and preventing XSS.
+ */
+ Helpers.markupEscape = (function(){
+ try {
+ var escapeMarkup = jQuery.fn.select2.defaults.escapeMarkup;
+ if(typeof escapeMarkup === "undefined") {
+ throw 'jQuery.fn.select2.defaults.escapeMarkup is undefined';
+ }
+ return escapeMarkup;
+ } catch (e){
+ console.log('Error: jQuery.fn.select2.defaults.escapeMarkup not found.\n' +
+ 'Exception: ' + e.toString());
+ throw e;
+ }
+ }());
+
/**
* replace wrong with right in text
*
@@ -231,12 +249,13 @@ window.OpenProject = (function ($) {
// fallback to base behavior
if (result.matches === undefined) {
- return replaceSpecialChars(format(result.text, query.term));
+ return replaceSpecialChars(
+ Helpers.markupEscape(format(result.text, query.term)));
}
// shortcut for empty searches
if (query.sterm.length === 0) {
- return result.text;
+ return Helpers.markupEscape(result.text);
}
var matches = result.matches.slice(),
@@ -248,7 +267,7 @@ window.OpenProject = (function ($) {
text = Helpers.replace(text, match[0], format(match[0], match[1]));
}
- return replaceSpecialChars(text);
+ return replaceSpecialChars(Helpers.markupEscape(text));
};
})();
diff --git a/app/assets/javascripts/timelines_autocompleter.js b/app/assets/javascripts/timelines_autocompleter.js
index a146557a85c2..85df82353e2b 100644
--- a/app/assets/javascripts/timelines_autocompleter.js
+++ b/app/assets/javascripts/timelines_autocompleter.js
@@ -124,18 +124,22 @@
markup = [];
if (match < 0) {
- return "" + item.name + "";
+ return "" +
+ OpenProject.Helpers.markupEscape(item.name) + "";
}
- markup.push(item.name.substring(0, match));
+ markup.push(OpenProject.Helpers.markupEscape(
+ item.name.substring(0, match)));
markup.push("");
- markup.push(item.name.substring(match, match + tl));
+ markup.push(OpenProject.Helpers.markupEscape(
+ item.name.substring(match, match + tl)));
markup.push("");
- markup.push(item.name.substring(match + tl, item.name.length));
- return markup.join("")
+ markup.push(OpenProject.Helpers.markupEscape(
+ item.name.substring(match + tl, item.name.length)));
+ return markup.join("");
},
formatSelection: function (item) {
- return item.name;
+ return OpenProject.Helpers.markupEscape(item.name);
},
initSelection: function (element, callback) {
var data = [];