diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ace8a79ae..1f689fdcc78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Enterprise Fixes: - [data-manager] Fixed editing an event whose key contains `&` creating undeletable duplicate rows in the events table Security Fixes: +- [push] The message editor now sanitizes message content before rendering it into the editor, allowing only the user-property token element and rendering any other markup as text - [hooks] Internal event hooks are now scoped to the apps the hook belongs to: app creation is a global-admin-only event, and remote-config, cohort, alert and hook-chaining events are only delivered when the event's app is one the hook is scoped to - [compliance-hub] The consents table now returns a fixed set of fields; a projection supplied on the request is no longer used to widen the response beyond the consent columns - [dashboards] Widgets are no longer copied when the copying user has no access to the apps they reference, and widget app ids are validated on widget create and update diff --git a/plugins/push/frontend/public/javascripts/countly.views.component.common.js b/plugins/push/frontend/public/javascripts/countly.views.component.common.js index 84082df30e1..765fd61b207 100644 --- a/plugins/push/frontend/public/javascripts/countly.views.component.common.js +++ b/plugins/push/frontend/public/javascripts/countly.views.component.common.js @@ -1,6 +1,15 @@ /* eslint-disable no-console */ /*global CV,countlyVue,countlyPushNotification,countlyGlobal,countlyCommon,moment*/ (function(countlyPushNotificationComponent) { + // The message editor is a live contenteditable. Its body is user-authored text; the + // only legitimate markup is the user-property token . Allow just that element + // (with the attributes the token relies on) and let everything else be escaped to inert + // text, so a stored message cannot introduce active markup when the editor is populated. + var PUSH_MESSAGE_EDITOR_XSS_OPTIONS = { + whiteList: { + span: ["class", "id", "contenteditable", "data-user-property-label", "data-user-property-value", "data-user-property-fallback"] + } + }; countlyPushNotificationComponent.LargeRadioButtonWithDescription = countlyVue.views.create({ props: { value: { @@ -706,7 +715,7 @@ }, reset: function(htmlContent, ids) { this.disconnectMutationObserver(); - this.$refs.element.innerHTML = htmlContent; + this.$refs.element.innerHTML = countlyCommon.encodeSomeHtml(htmlContent, PUSH_MESSAGE_EDITOR_XSS_OPTIONS); this.addEventListeners(ids); this.startMutationObserver(); },