diff --git a/horizon/static/horizon/js/horizon.instances.js b/horizon/static/horizon/js/horizon.instances.js
index 22d3b8950f..3e1aa42ddc 100644
--- a/horizon/static/horizon/js/horizon.instances.js
+++ b/horizon/static/horizon/js/horizon.instances.js
@@ -53,7 +53,7 @@ horizon.instances = {
$(this.get_network_element("")).each(function () {
var $this = $(this);
var $input = $this.children("input");
- var name = horizon.escape_html($this.text().replace(/^\s+/, ""));
+ var name = horizon.string.escapeHtml($this.text().replace(/^\s+/, ""));
var network_property = {
"name": name,
"id": $input.attr("id"),
diff --git a/horizon/static/horizon/js/horizon.js b/horizon/static/horizon/js/horizon.js
index d5a171f78f..66b8891a3c 100644
--- a/horizon/static/horizon/js/horizon.js
+++ b/horizon/static/horizon/js/horizon.js
@@ -28,16 +28,6 @@ var Horizon = function () {
initFunctions = [];
};
- /* An utility function for escaping HTML to avoid XSS. */
- horizon.escape_html = function (text) {
- return text.replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
- .replace(/'/g, ''')
- .replace(/\//g, '/');
- };
-
/* Storage for backend configuration variables which the frontend
* should be aware of.
*/
diff --git a/horizon/static/horizon/js/horizon.membership.js b/horizon/static/horizon/js/horizon.membership.js
index 8bebdc6b6c..71518af6bb 100644
--- a/horizon/static/horizon/js/horizon.membership.js
+++ b/horizon/static/horizon/js/horizon.membership.js
@@ -419,7 +419,7 @@ horizon.membership = {
horizon.membership.fix_stripes(step_slug);
},
'prepareQuery': function (val) {
- return new RegExp(val, "i");
+ return new RegExp(horizon.string.escapeRegex(val), "i");
},
'testQuery': function (query, txt, span) {
if ($(input).attr('id') === filter) {
diff --git a/horizon/static/horizon/js/horizon.string.js b/horizon/static/horizon/js/horizon.string.js
new file mode 100644
index 0000000000..92b68e4862
--- /dev/null
+++ b/horizon/static/horizon/js/horizon.string.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A namespace for string utility functions.
+ */
+horizon.string = {
+
+ /**
+ * Escapes any characters that would have special meaning in a regular expression.
+ */
+ escapeRegex: function(text) {
+ return text.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
+ },
+
+ /**
+ * Escapes any HTML characters to avoid XSS.
+ */
+ escapeHtml: function(text) {
+ return text.replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(/\//g, '/');
+ }
+};
diff --git a/horizon/static/horizon/js/horizon.tables.js b/horizon/static/horizon/js/horizon.tables.js
index 09dd6466f0..00288ebfb1 100644
--- a/horizon/static/horizon/js/horizon.tables.js
+++ b/horizon/static/horizon/js/horizon.tables.js
@@ -524,7 +524,7 @@ horizon.datatables.set_table_query_filter = function (parent) {
horizon.datatables.fix_row_striping(table);
},
prepareQuery: function (val) {
- return new RegExp(val, "i");
+ return new RegExp(horizon.string.escapeRegex(val), "i");
},
testQuery: function (query, txt, _row) {
return query.test($(_row).find('td:not(.hidden):not(.actions_column)').text());
diff --git a/horizon/static/horizon/tests/jasmine/string.legacy-spec.js b/horizon/static/horizon/tests/jasmine/string.legacy-spec.js
new file mode 100644
index 0000000000..de168867aa
--- /dev/null
+++ b/horizon/static/horizon/tests/jasmine/string.legacy-spec.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+describe("String utilities (horizon.string.js)", function() {
+
+ describe("Escape Regex", function() {
+ var escapeRegex = horizon.string.escapeRegex;
+ var noRegexChars = 'string with no regex chars';
+ var mixed = '\'$24.00 ^ ?"';
+ var allRegexChars = '.*+?^${}()[]|/\\';
+
+ it('should escape regular expression characters', function () {
+ expect(escapeRegex(noRegexChars)).toBe(noRegexChars);
+ expect(escapeRegex(mixed)).toBe('\'\\$24\\.00 \\^ \\?"');
+ expect(escapeRegex(allRegexChars)).toBe('\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\[\\]\\|\\/\\\\');
+ });
+ });
+
+ describe("Escape HTML", function() {
+ var escapeHtml = horizon.string.escapeHtml;
+ var noHtmlChars = 'string with no HTML chars';
+ var mixed = 'foo & bar';
+ var allHtmlChars = '&<>"\'/';
+
+ it('should escape HTML characters', function () {
+ expect(escapeHtml(noHtmlChars)).toBe(noHtmlChars);
+ expect(escapeHtml(mixed)).toBe('foo & <b>bar</b>');
+ expect(escapeHtml(allHtmlChars)).toBe('&<>"'/');
+ });
+ });
+});
diff --git a/horizon/templates/horizon/jasmine/jasmine_legacy.html b/horizon/templates/horizon/jasmine/jasmine_legacy.html
index f1525c7ef8..a3a92c601a 100644
--- a/horizon/templates/horizon/jasmine/jasmine_legacy.html
+++ b/horizon/templates/horizon/jasmine/jasmine_legacy.html
@@ -17,6 +17,7 @@
+
{% include "horizon/client_side/templates.html" %}
@@ -35,6 +36,7 @@
+
{% endblock %}
{% block content %}
diff --git a/openstack_dashboard/templates/horizon/_scripts.html b/openstack_dashboard/templates/horizon/_scripts.html
index 24c9be02a5..07b8ee188d 100644
--- a/openstack_dashboard/templates/horizon/_scripts.html
+++ b/openstack_dashboard/templates/horizon/_scripts.html
@@ -26,6 +26,7 @@
+