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 @@ +