From f3139313048be9c1d512788eb1903a116c2c8eaa Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Thu, 15 Feb 2018 09:57:13 +0100 Subject: [PATCH] SONAR-10438 Fix XSS issues related to select2 --- .../apps/coding-rules/bulk-change-modal-view.js | 15 +++++++++------ .../coding-rules/rule/profile-activation-view.js | 7 +++++-- .../js/apps/coding-rules/rule/rule-meta-view.js | 5 ++++- .../src/main/js/apps/custom-measures/form-view.js | 5 ++++- .../src/main/js/apps/metrics/form-view.js | 5 ++++- .../apps/permission-templates/views/FormView.js | 4 ---- .../project/views/ApplyTemplateView.js | 5 ++++- .../src/main/js/components/common/modal-form.js | 5 +++-- 8 files changed, 33 insertions(+), 18 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js index f9e5e728665..87a3c4ef21a 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/bulk-change-modal-view.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import escapeHtml from 'escape-html'; import ModalFormView from '../../components/common/modal-form'; import Template from './templates/coding-rules-bulk-change-modal.hbs'; import { translateWithParameters } from '../../helpers/l10n'; @@ -36,8 +37,8 @@ export default ModalFormView.extend({ const profileBase = this.options.app.qualityProfiles.find(p => p.key === profile); const message = translateWithParameters( 'coding_rules.bulk_change.success', - profileBase.name, - profileBase.language, + escapeHtml(profileBase.name), + escapeHtml(profileBase.language), succeeded ); this.ui.messagesContainer.append(`
${message}
`); @@ -47,8 +48,8 @@ export default ModalFormView.extend({ const profileBase = this.options.app.qualityProfiles.find(p => p.key === profile); const message = translateWithParameters( 'coding_rules.bulk_change.warning', - profileBase.name, - profileBase.language, + escapeHtml(profileBase.name), + escapeHtml(profileBase.language), succeeded, failed ); @@ -60,7 +61,8 @@ export default ModalFormView.extend({ this.$('#coding-rules-bulk-change-profile').select2({ width: '250px', minimumResultsForSearch: 1, - openOnEnter: false + openOnEnter: false, + escapeMarkup: markup => markup }); }, @@ -114,7 +116,8 @@ export default ModalFormView.extend({ } return profiles .filter(profile => profile.actions && profile.actions.edit) - .filter(profile => !profile.isBuiltIn); + .filter(profile => !profile.isBuiltIn) + .map(profile => ({ ...profile, name: escapeHtml(profile.name) })); }, serializeData() { diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js index c4c34051150..5b165f22c99 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/profile-activation-view.js @@ -19,6 +19,7 @@ */ import $ from 'jquery'; import Backbone from 'backbone'; +import escapeHtml from 'escape-html'; import ModalForm from '../../../components/common/modal-form'; import Template from '../templates/rule/coding-rules-profile-activation.hbs'; import { csvEscape } from '../../../helpers/csv'; @@ -49,7 +50,9 @@ export default ModalForm.extend({ this.ui.qualityProfileSelect.select2({ width: '250px', - minimumResultsForSearch: 5 + minimumResultsForSearch: 5, + escapeMarkup: escapeHtml, + formatResult: result => result.text }); const that = this; @@ -157,7 +160,7 @@ export default ModalForm.extend({ const availableProfiles = this.getAvailableQualityProfiles(this.options.rule.get('lang')); const contextProfile = this.options.app.state.get('query').qprofile; - // decrease depth by 1, so the top level starts at 0 + // decrease depth by 1, so the top level starts at 0, and escape name to prevent xss const profilesWithDepth = sortProfiles(availableProfiles).map(profile => ({ ...profile, depth: profile.depth - 1 diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js index 15d18464f5c..eed40367481 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-meta-view.js @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import $ from 'jquery'; +import escapeHtml from 'escape-html'; import { difference, union } from 'lodash'; import Marionette from 'backbone.marionette'; import RuleFilterMixin from './rule-filter-mixin'; @@ -62,7 +63,9 @@ export default Marionette.ItemView.extend(RuleFilterMixin).extend({ tags => { this.ui.tagInput.select2({ tags: difference(difference(tags, this.model.get('tags')), this.model.get('sysTags')), - width: '300px' + width: '300px', + escapeMarkup: escapeHtml, + formatResult: result => result.text }); this.ui.tagsEdit.removeClass('hidden'); diff --git a/server/sonar-web/src/main/js/apps/custom-measures/form-view.js b/server/sonar-web/src/main/js/apps/custom-measures/form-view.js index d89fa0e800b..b47bd51e1ef 100644 --- a/server/sonar-web/src/main/js/apps/custom-measures/form-view.js +++ b/server/sonar-web/src/main/js/apps/custom-measures/form-view.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import escapeHtml from 'escape-html'; import ModalForm from '../../components/common/modal-form'; import Metrics from '../metrics/metrics'; import Template from './templates/custom-measures-form.hbs'; @@ -35,7 +36,9 @@ export default ModalForm.extend({ this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); this.$('#create-custom-measure-metric').select2({ width: '250px', - minimumResultsForSearch: 20 + minimumResultsForSearch: 20, + escapeMarkup: escapeHtml, + formatResult: result => result.text }); }, diff --git a/server/sonar-web/src/main/js/apps/metrics/form-view.js b/server/sonar-web/src/main/js/apps/metrics/form-view.js index d0ab24e62e7..440105b4439 100644 --- a/server/sonar-web/src/main/js/apps/metrics/form-view.js +++ b/server/sonar-web/src/main/js/apps/metrics/form-view.js @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import $ from 'jquery'; +import escapeHtml from 'escape-html'; import ModalForm from '../../components/common/modal-form'; import Template from './templates/metrics-form.hbs'; @@ -47,7 +48,9 @@ export default ModalForm.extend({ return { id: item, text: item }; }); options.callback({ results, more: false }); - } + }, + escapeMarkup: escapeHtml, + formatResult: result => result.text }) .select2('val', this.model && this.model.get('domain')); this.$('#create-metric-type').select2({ width: '250px' }); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js b/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js index 1bf56cf1e01..4f41ec1f3f0 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js +++ b/server/sonar-web/src/main/js/apps/permission-templates/views/FormView.js @@ -26,10 +26,6 @@ export default ModalForm.extend({ onRender() { ModalForm.prototype.onRender.apply(this, arguments); this.$('[data-toggle="tooltip"]').tooltip({ container: 'body', placement: 'bottom' }); - this.$('#create-custom-measure-metric').select2({ - width: '250px', - minimumResultsForSearch: 20 - }); }, onDestroy() { diff --git a/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js b/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js index a7c8c972d0b..9d1b6e4409e 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js +++ b/server/sonar-web/src/main/js/apps/permissions/project/views/ApplyTemplateView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import escapeHtml from 'escape-html'; import ModalForm from '../../../../components/common/modal-form'; import { applyTemplateToProject, getPermissionTemplates } from '../../../../api/permissions'; import Template from '../templates/ApplyTemplateTemplate.hbs'; @@ -40,7 +41,9 @@ export default ModalForm.extend({ ModalForm.prototype.onRender.apply(this, arguments); this.$('#project-permissions-template').select2({ width: '250px', - minimumResultsForSearch: 20 + minimumResultsForSearch: 20, + escapeMarkup: escapeHtml, + formatResult: result => result.text }); }, diff --git a/server/sonar-web/src/main/js/components/common/modal-form.js b/server/sonar-web/src/main/js/components/common/modal-form.js index 0610c634f0a..e344fdcaff4 100644 --- a/server/sonar-web/src/main/js/components/common/modal-form.js +++ b/server/sonar-web/src/main/js/components/common/modal-form.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import escapeHtml from 'escape-html'; import ModalView from './modals'; export default ModalView.extend({ @@ -60,13 +61,13 @@ export default ModalView.extend({ const container = this.ui.messagesContainer.empty(); if (Array.isArray(errors)) { errors.forEach(error => { - const html = `
${error.msg}
`; + const html = `
${escapeHtml(error.msg)}
`; container.append(html); }); } if (Array.isArray(warnings)) { warnings.forEach(warn => { - const html = `
${warn.msg}
`; + const html = `
${escapeHtml(warn.msg)}
`; container.append(html); }); } -- 2.39.5