From d115fda4e0d1b74a8eeb958dfe6b99d735791c4e Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Wed, 19 Apr 2023 10:40:33 -0500 Subject: [PATCH] UI: Remove usage of htmlSafe (#20235) --- changelog/20235.txt | 3 ++ ui/app/components/diff-version-selector.js | 5 ++- ui/app/components/wizard-content.js | 9 +++-- .../components/wizard/features-selection.js | 5 ++- .../components/diff-version-selector.hbs | 2 +- .../templates/components/wizard-progress.hbs | 2 +- ui/lib/core/addon/components/confirm.js | 3 +- .../addon/components/replication-dashboard.js | 7 ++-- ui/lib/core/addon/helpers/sanitized-html.js | 14 ++++++++ .../components/replication-dashboard.hbs | 2 +- ui/lib/core/app/helpers/sanitized-html.js | 1 + ui/lib/core/package.json | 1 + .../components/page/role/create-and-edit.hbs | 2 +- .../components/page/role/create-and-edit.js | 3 +- ui/package.json | 1 + .../helpers/sanitized-html-test.js | 35 +++++++++++++++++++ ui/yarn.lock | 5 +++ 17 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 changelog/20235.txt create mode 100644 ui/lib/core/addon/helpers/sanitized-html.js create mode 100644 ui/lib/core/app/helpers/sanitized-html.js create mode 100644 ui/tests/integration/helpers/sanitized-html-test.js diff --git a/changelog/20235.txt b/changelog/20235.txt new file mode 100644 index 000000000..d1b9f8a6e --- /dev/null +++ b/changelog/20235.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: remove use of htmlSafe except when first sanitized +``` diff --git a/ui/app/components/diff-version-selector.js b/ui/app/components/diff-version-selector.js index 8daed6d32..dd0069fb7 100644 --- a/ui/app/components/diff-version-selector.js +++ b/ui/app/components/diff-version-selector.js @@ -8,7 +8,6 @@ import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; -import { htmlSafe } from '@ember/template'; /** * @module DiffVersionSelector @@ -64,10 +63,10 @@ export default class DiffVersionSelector extends Component { if (delta === undefined) { this.statesMatch = true; // params: value, replacer (all properties included), space (white space and indentation, line break, etc.) - this.visualDiff = htmlSafe(JSON.stringify(leftSideVersionData, undefined, 2)); + this.visualDiff = JSON.stringify(leftSideVersionData, undefined, 2); } else { this.statesMatch = false; - this.visualDiff = htmlSafe(jsondiffpatch.formatters.html.format(delta, rightSideVersionData)); + this.visualDiff = jsondiffpatch.formatters.html.format(delta, rightSideVersionData); } } diff --git a/ui/app/components/wizard-content.js b/ui/app/components/wizard-content.js index 40615ed05..8cee2e9bf 100644 --- a/ui/app/components/wizard-content.js +++ b/ui/app/components/wizard-content.js @@ -8,7 +8,6 @@ import { inject as service } from '@ember/service'; import Component from '@ember/component'; import { computed } from '@ember/object'; import { FEATURE_MACHINE_STEPS, INIT_STEPS } from 'vault/helpers/wizard-constants'; -import { htmlSafe } from '@ember/template'; export default Component.extend({ wizard: service(), @@ -89,25 +88,25 @@ export default Component.extend({ const bar = []; if (this.currentTutorialProgress) { bar.push({ - style: htmlSafe(`width:${this.currentTutorialProgress.percentage}%;`), + style: `width:${this.currentTutorialProgress.percentage}%;`, completed: false, showIcon: true, }); } else { if (this.currentFeatureProgress) { this.completedFeatures.forEach((feature) => { - bar.push({ style: htmlSafe('width:100%;'), completed: true, feature: feature, showIcon: true }); + bar.push({ style: 'width:100%;', completed: true, feature: feature, showIcon: true }); }); this.wizard.featureList.forEach((feature) => { if (feature === this.currentMachine) { bar.push({ - style: htmlSafe(`width:${this.currentFeatureProgress.percentage}%;`), + style: `width:${this.currentFeatureProgress.percentage}%;`, completed: this.currentFeatureProgress.percentage == 100 ? true : false, feature: feature, showIcon: true, }); } else { - bar.push({ style: htmlSafe('width:0%;'), completed: false, feature: feature, showIcon: true }); + bar.push({ style: 'width:0%;', completed: false, feature: feature, showIcon: true }); } }); } diff --git a/ui/app/components/wizard/features-selection.js b/ui/app/components/wizard/features-selection.js index 944b1d9c7..a14e99318 100644 --- a/ui/app/components/wizard/features-selection.js +++ b/ui/app/components/wizard/features-selection.js @@ -8,7 +8,6 @@ import { inject as service } from '@ember/service'; import Component from '@ember/component'; import { computed } from '@ember/object'; import { FEATURE_MACHINE_TIME } from 'vault/helpers/wizard-constants'; -import { htmlSafe } from '@ember/template'; export default Component.extend({ wizard: service(), @@ -55,10 +54,10 @@ export default Component.extend({ }), selectProgress: computed('selectedFeatures', function () { let bar = this.selectedFeatures.map((feature) => { - return { style: htmlSafe('width:0%;'), completed: false, showIcon: true, feature: feature }; + return { style: 'width:0%;', completed: false, showIcon: true, feature: feature }; }); if (bar.length === 0) { - bar = [{ style: htmlSafe('width:0%;'), showIcon: false }]; + bar = [{ style: 'width:0%;', showIcon: false }]; } return bar; }), diff --git a/ui/app/templates/components/diff-version-selector.hbs b/ui/app/templates/components/diff-version-selector.hbs index 6ac766ff8..9114f3886 100644 --- a/ui/app/templates/components/diff-version-selector.hbs +++ b/ui/app/templates/components/diff-version-selector.hbs @@ -98,5 +98,5 @@
-
{{this.visualDiff}}
+
{{sanitized-html this.visualDiff}}
\ No newline at end of file diff --git a/ui/app/templates/components/wizard-progress.hbs b/ui/app/templates/components/wizard-progress.hbs index 6bae1819f..29899c5db 100644 --- a/ui/app/templates/components/wizard-progress.hbs +++ b/ui/app/templates/components/wizard-progress.hbs @@ -2,7 +2,7 @@ {{#each @progressBar as |bar|}}
- + {{#if bar.showIcon}} diff --git a/ui/lib/core/addon/components/confirm.js b/ui/lib/core/addon/components/confirm.js index a287b78af..f7511ce42 100644 --- a/ui/lib/core/addon/components/confirm.js +++ b/ui/lib/core/addon/components/confirm.js @@ -5,7 +5,6 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; -import { htmlSafe } from '@ember/template'; import layout from '../templates/components/confirm'; import { next } from '@ember/runloop'; @@ -34,7 +33,7 @@ export default Component.extend({ height: 0, focusTrigger: null, style: computed('height', function () { - return htmlSafe(`height: ${this.height}px`); + return `height: ${this.height}px`; }), wormholeReference: null, wormholeId: computed('elementId', function () { diff --git a/ui/lib/core/addon/components/replication-dashboard.js b/ui/lib/core/addon/components/replication-dashboard.js index ff9e5e8d3..1993c6d57 100644 --- a/ui/lib/core/addon/components/replication-dashboard.js +++ b/ui/lib/core/addon/components/replication-dashboard.js @@ -7,7 +7,6 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; import { clusterStates } from 'core/helpers/cluster-states'; import { capitalize } from '@ember/string'; -import { htmlSafe } from '@ember/template'; import layout from '../templates/components/replication-dashboard'; /** @@ -35,7 +34,7 @@ import layout from '../templates/components/replication-dashboard'; * @param {Boolean} [isSummaryDashboard=false] - Only true when the cluster is both a dr and performance primary. If true, replicationDetailsSummary is populated and used to pass through the cluster details. * @param {Object} replicationDetailsSummary=null - An Ember data object computed off the Ember Model. It combines the Model.dr and Model.performance objects into one and contains details specific to the mode replication. * @param {Object} replicationDetails=null - An Ember data object pulled from the Ember Model. It contains details specific to the whether the replication is dr or performance. - * @param {String} clusterMode=null - The cluster mode passed through to a table component. + * @param {String} clusterMode=null - The cluster mode passed through to a table component. * @param {Object} reindexingDetails=null - An Ember data object used to show a reindexing progress bar. */ @@ -94,9 +93,7 @@ export default Component.extend({ }), reindexMessage: computed('isSecondary', 'progressBar', function () { if (!this.isSecondary) { - return htmlSafe( - 'This can cause a delay depending on the size of the data store. You can not use Vault during this time.' - ); + return 'This can cause a delay depending on the size of the data store. You can not use Vault during this time.'; } return 'This can cause a delay depending on the size of the data store. You can use Vault during this time.'; }), diff --git a/ui/lib/core/addon/helpers/sanitized-html.js b/ui/lib/core/addon/helpers/sanitized-html.js new file mode 100644 index 000000000..cfa99d723 --- /dev/null +++ b/ui/lib/core/addon/helpers/sanitized-html.js @@ -0,0 +1,14 @@ +import { helper } from '@ember/component/helper'; +import { debug } from '@ember/debug'; +import { htmlSafe } from '@ember/template'; +import { sanitize } from 'dompurify'; + +export default helper(function sanitizedHtml([htmlString]) { + try { + return htmlSafe(sanitize(htmlString)); + } catch (e) { + debug('Error sanitizing string', e); + // I couldn't get this to actually fail but as a fallback render the value as-is + return htmlString; + } +}); diff --git a/ui/lib/core/addon/templates/components/replication-dashboard.hbs b/ui/lib/core/addon/templates/components/replication-dashboard.hbs index 834619b09..8e0aaef58 100644 --- a/ui/lib/core/addon/templates/components/replication-dashboard.hbs +++ b/ui/lib/core/addon/templates/components/replication-dashboard.hbs @@ -5,7 +5,7 @@ @title={{concat "Re-indexing in progress" this.reindexingStage}} @type="info" @progressBar={{this.progressBar}} - @message={{this.reindexMessage}} + @message={{sanitized-html this.reindexMessage}} data-test-isReindexing />
diff --git a/ui/lib/core/app/helpers/sanitized-html.js b/ui/lib/core/app/helpers/sanitized-html.js new file mode 100644 index 000000000..7bd6cb8d9 --- /dev/null +++ b/ui/lib/core/app/helpers/sanitized-html.js @@ -0,0 +1 @@ +export { default } from 'core/helpers/sanitized-html'; diff --git a/ui/lib/core/package.json b/ui/lib/core/package.json index b4326d2e2..3fcb0c734 100644 --- a/ui/lib/core/package.json +++ b/ui/lib/core/package.json @@ -8,6 +8,7 @@ "date-fns": "*", "@icholy/duration": "*", "base64-js": "*", + "dompurify": "*", "ember-auto-import": "*", "ember-basic-dropdown": "*", "ember-cli-babel": "*", diff --git a/ui/lib/kubernetes/addon/components/page/role/create-and-edit.hbs b/ui/lib/kubernetes/addon/components/page/role/create-and-edit.hbs index ac2780d6b..fad90586b 100644 --- a/ui/lib/kubernetes/addon/components/page/role/create-and-edit.hbs +++ b/ui/lib/kubernetes/addon/components/page/role/create-and-edit.hbs @@ -109,7 +109,7 @@ @value={{template.rules}} @mode="ruby" @valueUpdated={{fn (mut template.rules)}} - @helpText={{this.roleRulesHelpText}} + @helpText={{sanitized-html this.roleRulesHelpText}} >