Skip to content

Commit ff8288d

Browse files
authored
cherry-pick(#7144): fix(#7143): add eslint-plugin-no-unsanitized and fix errors (#7148)
1 parent c1dd82c commit ff8288d

File tree

11 files changed

+115
-65
lines changed

11 files changed

+115
-65
lines changed

.eslintrc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ module.exports = {
1515
'plugin:compat/recommended',
1616
'plugin:vue/vue3-recommended',
1717
'plugin:you-dont-need-lodash-underscore/compatible',
18-
'plugin:prettier/recommended'
18+
'plugin:prettier/recommended',
19+
'plugin:no-unsanitized/DOM'
1920
],
2021
parser: 'vue-eslint-parser',
2122
parserOptions: {

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"eslint": "8.48.0",
2828
"eslint-config-prettier": "9.0.0",
2929
"eslint-plugin-compat": "4.2.0",
30+
"eslint-plugin-no-unsanitized": "4.0.2",
3031
"eslint-plugin-playwright": "0.12.0",
3132
"eslint-plugin-prettier": "4.2.1",
3233
"eslint-plugin-simple-import-sort": "10.0.0",

src/plugins/flexibleLayout/components/Frame.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,11 @@ export default {
161161
let originalClassName = this.dragGhost.classList[0];
162162
this.dragGhost.className = '';
163163
this.dragGhost.classList.add(originalClassName, iconClass);
164+
this.dragGhost.textContent = '';
165+
const span = document.createElement('span');
166+
span.textContent = this.domainObject.name;
167+
this.dragGhost.appendChild(span);
164168

165-
this.dragGhost.innerHTML = `<span>${this.domainObject.name}</span>`;
166169
event.dataTransfer.setDragImage(this.dragGhost, 0, 0);
167170
}
168171

src/plugins/plot/chart/MctChart.vue

+16-7
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@
2020
at runtime from the About dialog for additional information.
2121
-->
2222

23-
<!-- eslint-disable vue/no-v-html -->
24-
2523
<template>
2624
<div class="gl-plot-chart-area">
27-
<span v-html="canvasTemplate"></span>
28-
<span v-html="canvasTemplate"></span>
25+
<canvas :style="canvasStyle"></canvas>
26+
<canvas :style="canvasStyle"></canvas>
2927
<div ref="limitArea" class="js-limit-area">
3028
<limit-label
3129
v-for="(limitLabel, index) in visibleLimitLabels"
@@ -146,12 +144,20 @@ export default {
146144
},
147145
data() {
148146
return {
149-
canvasTemplate:
150-
'<canvas style="position: absolute; background: none; width: 100%; height: 100%;"></canvas>',
151147
visibleLimitLabels: [],
152148
visibleLimitLines: []
153149
};
154150
},
151+
computed: {
152+
canvasStyle() {
153+
return {
154+
position: 'absolute',
155+
background: 'none',
156+
width: '100%',
157+
height: '100%'
158+
};
159+
}
160+
},
155161
watch: {
156162
highlights: {
157163
handler() {
@@ -487,7 +493,10 @@ export default {
487493
// Have to throw away the old canvas elements and replace with new
488494
// canvas elements in order to get new drawing contexts.
489495
const div = document.createElement('div');
490-
div.innerHTML = this.canvasTemplate + this.canvasTemplate;
496+
div.innerHTML = `
497+
<canvas style="position: absolute; background: none; width: 100%; height: 100%;"></canvas>
498+
<canvas style="position: absolute; background: none; width: 100%; height: 100%;"></canvas>
499+
`;
491500
const mainCanvas = div.querySelectorAll('canvas')[1];
492501
const overlayCanvas = div.querySelectorAll('canvas')[0];
493502
this.canvas.parentNode.replaceChild(mainCanvas, this.canvas);

src/plugins/summaryWidget/src/Condition.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ define([
214214
const options = this.generateSelectOptions();
215215

216216
newInput = document.createElement('select');
217-
newInput.innerHTML = options;
217+
newInput.appendChild(options);
218218

219219
emitChange = true;
220220
} else {
@@ -244,12 +244,16 @@ define([
244244

245245
Condition.prototype.generateSelectOptions = function () {
246246
let telemetryMetadata = this.conditionManager.getTelemetryMetadata(this.config.object);
247-
let options = '';
247+
let fragment = document.createDocumentFragment();
248+
248249
telemetryMetadata[this.config.key].enumerations.forEach((enumeration) => {
249-
options += '<option value="' + enumeration.value + '">' + enumeration.string + '</option>';
250+
const option = document.createElement('option');
251+
option.value = enumeration.value;
252+
option.textContent = enumeration.string;
253+
fragment.appendChild(option);
250254
});
251255

252-
return options;
256+
return fragment;
253257
};
254258

255259
return Condition;

src/plugins/summaryWidget/src/Rule.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ define([
167167
const ruleHeader = self.domElement
168168
.querySelectorAll('.widget-rule-header')[0]
169169
.cloneNode(true);
170-
indicator.innerHTML = ruleHeader;
170+
indicator.textContent = '';
171+
indicator.appendChild(ruleHeader);
171172
});
172173
self.widgetDnD.setDragImage(
173174
self.domElement.querySelectorAll('.widget-rule-header')[0].cloneNode(true)
@@ -239,8 +240,8 @@ define([
239240
this.listenTo(this.toggleConfigButton, 'click', toggleConfig);
240241
this.listenTo(this.trigger, 'change', onTriggerInput);
241242

242-
this.title.innerHTML = self.config.name;
243-
this.description.innerHTML = self.config.description;
243+
this.title.innerText = self.config.name;
244+
this.description.innerText = self.config.description;
244245
this.trigger.value = self.config.trigger;
245246

246247
this.listenTo(this.grippy, 'mousedown', onDragStart);
@@ -456,7 +457,7 @@ define([
456457
const lastOfType = self.conditionArea.querySelector('li:last-of-type');
457458
lastOfType.parentNode.insertBefore($condition, lastOfType);
458459
if (loopCnt > 0) {
459-
$condition.querySelector('.t-condition-context').innerHTML = triggerContextStr + ' when';
460+
$condition.querySelector('.t-condition-context').innerText = triggerContextStr + ' when';
460461
}
461462

462463
loopCnt++;
@@ -528,7 +529,7 @@ define([
528529
}
529530

530531
description = description === '' ? this.config.description : description;
531-
this.description.innerHTML = self.config.description;
532+
this.description.innerText = self.config.description;
532533
this.config.description = description;
533534
};
534535

src/plugins/summaryWidget/src/SummaryWidget.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -247,9 +247,10 @@ define([
247247
SummaryWidget.prototype.updateWidget = function () {
248248
const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon';
249249
const activeRule = this.rulesById[this.activeId];
250+
250251
this.applyStyle(this.domElement.querySelector('#widget'), activeRule.getProperty('style'));
251252
this.domElement.querySelector('#widget').title = activeRule.getProperty('message');
252-
this.domElement.querySelector('#widgetLabel').innerHTML = activeRule.getProperty('label');
253+
this.domElement.querySelector('#widgetLabel').textContent = activeRule.getProperty('label');
253254
this.domElement.querySelector('#widgetIcon').classList =
254255
WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon');
255256
};

src/plugins/summaryWidget/src/input/Palette.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ define([
4444
self.setNullOption(this.nullOption);
4545

4646
self.items.forEach(function (item) {
47-
const itemElement = `<div class = "c-palette__item ${item}" data-item = "${item}"></div>`;
48-
const temp = document.createElement('div');
49-
temp.innerHTML = itemElement;
50-
self.itemElements[item] = temp.firstChild;
51-
self.domElement.querySelector('.c-palette__items').appendChild(temp.firstChild);
47+
const itemElement = document.createElement('div');
48+
itemElement.className = 'c-palette__item ' + item;
49+
itemElement.setAttribute('data-item', item);
50+
51+
self.itemElements[item] = itemElement;
52+
self.domElement.querySelector('.c-palette__items').appendChild(itemElement);
5253
});
5354

5455
self.domElement.querySelector('.c-menu').style.display = 'none';
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,60 @@
1-
define(['./summary-widget.html', '@braintree/sanitize-url'], function (
2-
summaryWidgetTemplate,
3-
urlSanitizeLib
4-
) {
5-
const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon';
1+
import * as urlSanitizeLib from '@braintree/sanitize-url';
62

7-
function SummaryWidgetView(domainObject, openmct) {
3+
const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon';
4+
5+
class SummaryWidgetView {
6+
#createSummaryWidgetTemplate() {
7+
const anchor = document.createElement('a');
8+
anchor.classList.add(
9+
't-summary-widget',
10+
'c-summary-widget',
11+
'js-sw',
12+
'u-links',
13+
'u-fills-container'
14+
);
15+
16+
const widgetIcon = document.createElement('div');
17+
widgetIcon.id = 'widgetIcon';
18+
widgetIcon.classList.add('c-sw__icon', 'js-sw__icon');
19+
anchor.appendChild(widgetIcon);
20+
21+
const widgetLabel = document.createElement('div');
22+
widgetLabel.id = 'widgetLabel';
23+
widgetLabel.classList.add('c-sw__label', 'js-sw__label');
24+
widgetLabel.textContent = 'Loading...';
25+
anchor.appendChild(widgetLabel);
26+
27+
return anchor;
28+
}
29+
30+
constructor(domainObject, openmct) {
831
this.openmct = openmct;
932
this.domainObject = domainObject;
1033
this.hasUpdated = false;
1134
this.render = this.render.bind(this);
1235
}
1336

14-
SummaryWidgetView.prototype.updateState = function (datum) {
37+
updateState(datum) {
1538
this.hasUpdated = true;
1639
this.widget.style.color = datum.textColor;
1740
this.widget.style.backgroundColor = datum.backgroundColor;
1841
this.widget.style.borderColor = datum.borderColor;
1942
this.widget.title = datum.message;
2043
this.label.title = datum.message;
21-
this.label.innerHTML = datum.ruleLabel;
44+
this.label.textContent = datum.ruleLabel;
2245
this.icon.className = WIDGET_ICON_CLASS + ' ' + datum.icon;
23-
};
46+
}
2447

25-
SummaryWidgetView.prototype.render = function () {
48+
render() {
2649
if (this.unsubscribe) {
2750
this.unsubscribe();
2851
}
2952

3053
this.hasUpdated = false;
3154

32-
this.container.innerHTML = summaryWidgetTemplate;
55+
const anchor = this.#createSummaryWidgetTemplate();
56+
this.container.appendChild(anchor);
57+
3358
this.widget = this.container.querySelector('a');
3459
this.icon = this.container.querySelector('#widgetIcon');
3560
this.label = this.container.querySelector('.js-sw__label');
@@ -49,33 +74,32 @@ define(['./summary-widget.html', '@braintree/sanitize-url'], function (
4974

5075
const renderTracker = {};
5176
this.renderTracker = renderTracker;
77+
5278
this.openmct.telemetry
5379
.request(this.domainObject, {
5480
strategy: 'latest',
5581
size: 1
5682
})
57-
.then(
58-
function (results) {
59-
if (
60-
this.destroyed ||
61-
this.hasUpdated ||
62-
this.renderTracker !== renderTracker ||
63-
results.length === 0
64-
) {
65-
return;
66-
}
67-
68-
this.updateState(results[results.length - 1]);
69-
}.bind(this)
70-
);
83+
.then((results) => {
84+
if (
85+
this.destroyed ||
86+
this.hasUpdated ||
87+
this.renderTracker !== renderTracker ||
88+
results.length === 0
89+
) {
90+
return;
91+
}
92+
93+
this.updateState(results[results.length - 1]);
94+
});
7195

7296
this.unsubscribe = this.openmct.telemetry.subscribe(
7397
this.domainObject,
7498
this.updateState.bind(this)
7599
);
76-
};
100+
}
77101

78-
SummaryWidgetView.prototype.show = function (container) {
102+
show(container) {
79103
this.container = container;
80104
this.render();
81105
this.removeMutationListener = this.openmct.objects.observe(
@@ -84,14 +108,14 @@ define(['./summary-widget.html', '@braintree/sanitize-url'], function (
84108
this.onMutation.bind(this)
85109
);
86110
this.openmct.time.on('timeSystem', this.render);
87-
};
111+
}
88112

89-
SummaryWidgetView.prototype.onMutation = function (domainObject) {
113+
onMutation(domainObject) {
90114
this.domainObject = domainObject;
91115
this.render();
92-
};
116+
}
93117

94-
SummaryWidgetView.prototype.destroy = function (container) {
118+
destroy() {
95119
this.unsubscribe();
96120
this.removeMutationListener();
97121
this.openmct.time.off('timeSystem', this.render);
@@ -100,7 +124,7 @@ define(['./summary-widget.html', '@braintree/sanitize-url'], function (
100124
delete this.label;
101125
delete this.openmct;
102126
delete this.domainObject;
103-
};
127+
}
128+
}
104129

105-
return SummaryWidgetView;
106-
});
130+
export default SummaryWidgetView;

src/plugins/summaryWidget/src/views/summary-widget.html

-4
This file was deleted.

src/utils/template/templateHelpers.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
export function convertTemplateToHTML(templateString) {
2-
const template = document.createElement('template');
3-
template.innerHTML = templateString;
2+
const parser = new DOMParser();
3+
const doc = parser.parseFromString(templateString, 'text/html');
44

5-
return template.content.cloneNode(true).children;
5+
// Create a document fragment to hold the parsed content
6+
const fragment = document.createDocumentFragment();
7+
8+
// Append nodes from the parsed content to the fragment
9+
while (doc.body.firstChild) {
10+
fragment.appendChild(doc.body.firstChild);
11+
}
12+
13+
// Convert children of the fragment to an array and return
14+
return Array.from(fragment.children);
615
}
716

817
export function toggleClass(element, className) {

0 commit comments

Comments
 (0)