Skip to content

Commit cce2e96

Browse files
committed
sanitize user input when passing it to innerHTML in the UI
* use dompurify library for that * replaced many `innerHTML` assignments with `textContent` Signed-off-by: Thomas Jäckle <[email protected]>
1 parent aba53ad commit cce2e96

17 files changed

+66
-68
lines changed

ui/modules/connections/connections.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
/* eslint-disable require-jsdoc */
1717
import * as API from '../api.js';
1818
import * as Utils from '../utils.js';
19-
import {TabHandler} from '../utils/tabHandler.js';
19+
import { TabHandler } from '../utils/tabHandler.js';
2020
import connectionsHTML from './connections.html';
2121

2222
const observers = [];
@@ -57,7 +57,7 @@ export function setConnection(connection, isNewConnection = false) {
5757
}
5858

5959
export function loadConnections() {
60-
dom.tbodyConnections.innerHTML = '';
60+
dom.tbodyConnections.textContent = '';
6161
let connectionSelected = false;
6262
API.callConnectionsAPI('listConnections', (connections) => {
6363
connections.forEach((connection) => {
@@ -66,15 +66,15 @@ export function loadConnections() {
6666
row.id = id;
6767
if (API.env() === 'ditto_2') {
6868
API.callConnectionsAPI('retrieveConnection', (dittoConnection) => {
69-
row.insertCell(0).innerHTML = dittoConnection.name;
69+
row.insertCell(0).textContent = dittoConnection.name;
7070
},
7171
id);
7272
} else {
73-
row.insertCell(0).innerHTML = connection.name ? connection.name : id;
73+
row.insertCell(0).textContent = connection.name ? connection.name : id;
7474
}
7575
API.callConnectionsAPI('retrieveStatus', (status) => {
76-
row.insertCell(-1).innerHTML = status.liveStatus;
77-
row.insertCell(-1).innerHTML = status.recoveryStatus;
76+
row.insertCell(-1).textContent = status.liveStatus;
77+
row.insertCell(-1).textContent = status.recoveryStatus;
7878
},
7979
id);
8080
if (id === selectedConnectionId) {

ui/modules/connections/connectionsMonitor.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ function onEnableConnectionLogsClick() {
8686

8787
function retrieveConnectionMetrics() {
8888
Utils.assert(selectedConnectionId, 'Please select a connection', dom.tableValidationConnections);
89-
dom.tbodyConnectionMetrics.innerHTML = '';
89+
dom.tbodyConnectionMetrics.textContent = '';
9090
API.callConnectionsAPI('retrieveConnectionMetrics', (response) => {
9191
if (response.connectionMetrics) {
9292
Object.keys(response.connectionMetrics).forEach((direction) => {
@@ -149,8 +149,8 @@ function onConnectionChange(connection, isNewConnection = true) {
149149
selectedConnectionId = connection ? connection.id : null;
150150
connectionStatusDetail.setValue('');
151151
connectionLogDetail.setValue('');
152-
dom.tbodyConnectionMetrics.innerHTML = '';
153-
dom.tbodyConnectionLogs.innerHTML = '';
152+
dom.tbodyConnectionMetrics.textContent = '';
153+
dom.tbodyConnectionLogs.textContent = '';
154154
if (!isNewConnection && connection && connection.id) {
155155
retrieveConnectionLogs();
156156
}

ui/modules/environments/environments.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
* SPDX-License-Identifier: EPL-2.0
1212
*/
1313

14+
import * as Utils from '../utils.js';
1415
/* eslint-disable arrow-parens */
1516
/* eslint-disable prefer-const */
1617
/* eslint-disable require-jsdoc */
1718
import * as Authorization from './authorization.js';
18-
import * as Utils from '../utils.js';
19-
import defaultTemplates from './environmentTemplates.json';
2019
import environmentsHTML from './environments.html';
20+
import defaultTemplates from './environmentTemplates.json';
2121

2222

2323
const URL_PRIMARY_ENVIRONMENT_NAME = 'primaryEnvironmentName';
@@ -222,7 +222,7 @@ export function environmentsJsonChanged(modifiedField = null) {
222222
}
223223

224224
function updateEnvTable() {
225-
dom.tbodyEnvironments.innerHTML = '';
225+
dom.tbodyEnvironments.textContent = '';
226226
Object.keys(environments).forEach((key) => {
227227
Utils.addTableRow(dom.tbodyEnvironments, key, key === selectedEnvName);
228228
});

ui/modules/operations/operations.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,12 @@ function loadAllLogLevels() {
4747
API.callDittoREST('GET', '/devops/logging', null, null, false, true)
4848
.then((result) => createLoggerView(result))
4949
.catch((error) => {
50-
dom.divLoggers.innerHTML = '';
50+
dom.divLoggers.textContent = '';
5151
});
5252
}
5353

5454
function createLoggerView(allLogLevels) {
55-
dom.divLoggers.innerHTML = '';
55+
dom.divLoggers.textContent = '';
5656

5757
type LogLevel = {
5858
loggerConfigs?: object[]

ui/modules/policies/policies.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ function onThingChanged(thing) {
227227
}
228228

229229
function refreshWhoAmI() {
230-
dom.tbodyWhoami.innerHTML = '';
230+
dom.tbodyWhoami.textContent = '';
231231
API.callDittoREST('GET', '/whoami')
232232
.then((whoamiResult) => {
233233
whoamiResult.subjects.forEach((subject) => {

ui/modules/things/attributes.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function refreshAttribute(thing, attributePath = null) {
115115
function onThingChanged(thing) {
116116
dom.crudAttribute.editDisabled = (thing === null);
117117

118-
dom.tbodyAttributes.innerHTML = '';
118+
dom.tbodyAttributes.textContent = '';
119119
let count = 0;
120120
let thingHasAttribute = false;
121121
if (thing && thing.attributes) {

ui/modules/things/featureMessages.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,11 @@ function clearAllFields() {
163163
dom.inputMessageTimeout.value = '10';
164164
acePayload.setValue('');
165165
aceResponse.setValue('');
166-
dom.ulMessageTemplates.innerHTML = '';
166+
dom.ulMessageTemplates.textContent = '';
167167
}
168168

169169
function refillTemplates() {
170-
dom.ulMessageTemplates.innerHTML = '';
170+
dom.ulMessageTemplates.textContent = '';
171171
Utils.addDropDownEntries(dom.ulMessageTemplates, ['Saved message templates'], true);
172172
if (theFeatureId && Environments.current().messageTemplates[theFeatureId]) {
173173
Utils.addDropDownEntries(

ui/modules/things/features.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ function refreshFeature(thing, featureId = null) {
219219
function onThingChanged(thing) {
220220
dom.crudFeature.editDisabled = (thing === null);
221221
// Update features table
222-
dom.tbodyFeatures.innerHTML = '';
222+
dom.tbodyFeatures.textContent = '';
223223
let count = 0;
224224
let thingHasFeature = false;
225225
if (thing && thing.features) {

ui/modules/things/fields.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
* SPDX-License-Identifier: EPL-2.0
1212
*/
13-
import {Modal} from 'bootstrap';
13+
import { Modal } from 'bootstrap';
1414

1515
import * as Environments from '../environments/environments.js';
1616
import * as Utils from '../utils.js';
@@ -165,14 +165,14 @@ function onEnvironmentChanged() {
165165
* (Re-)Initializes the fieldlist in the UI
166166
*/
167167
function updateFieldList() {
168-
dom.fieldList.innerHTML = '';
168+
dom.fieldList.textContent = '';
169169
theFieldIndex = -1;
170170
Environments.current().fieldList.forEach((field, i) => {
171171
const fieldSelected = dom.fieldPath.value === field.path;
172172
const row = dom.fieldList.insertRow();
173173
Utils.addCheckboxToRow(row, i, field.active, toggleFieldActiveEventHandler);
174-
row.insertCell(-1).innerHTML = field.path;
175-
row.insertCell(-1).innerHTML = field['label'] ? field.label : null;
174+
row.insertCell(-1).textContent = field.path;
175+
row.insertCell(-1).textContent = field['label'] ? field.label : null;
176176
if (fieldSelected) {
177177
theFieldIndex = i;
178178
row.classList.add('table-active');

ui/modules/things/messagesIncoming.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function onMessageTableClick(event) {
5353
function onResetMessagesClick() {
5454
messages = [];
5555
dom.badgeMessageIncomingCount.textContent = '';
56-
dom.tbodyMessagesIncoming.innerHTML = '';
56+
dom.tbodyMessagesIncoming.textContent = '';
5757
messageDetail.setValue('');
5858
}
5959

ui/modules/things/searchFilter.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ export async function ready() {
6161
});
6262

6363
dom.searchThings.onclick = () => {
64-
fillHistory(dom.searchFilterEdit.value);
65-
ThingsSearch.searchTriggered(dom.searchFilterEdit.value);
64+
ThingsSearch.searchTriggered(dom.searchFilterEdit.value, () => fillHistory(dom.searchFilterEdit.value));
6665
};
6766

6867
dom.searchFavorite.onclick = () => {
@@ -74,8 +73,7 @@ export async function ready() {
7473

7574
dom.searchFilterEdit.onkeyup = (event) => {
7675
if ((event.key === 'Enter' || event.code === 13) && dom.searchFilterEdit.value.indexOf(FILTER_PLACEHOLDER) < 0) {
77-
fillHistory(dom.searchFilterEdit.value);
78-
ThingsSearch.searchTriggered(dom.searchFilterEdit.value);
76+
ThingsSearch.searchTriggered(dom.searchFilterEdit.value, () => fillHistory(dom.searchFilterEdit.value));
7977
} else {
8078
clearTimeout(keyStrokeTimeout);
8179
keyStrokeTimeout = setTimeout(checkIfFavorite, 1000);
@@ -107,7 +105,7 @@ function fillSearchFilterEdit(fillString) {
107105
checkIfFavorite();
108106
const filterEditNeeded = checkAndMarkParameter();
109107
if (!filterEditNeeded) {
110-
ThingsSearch.searchTriggered(dom.searchFilterEdit.value);
108+
ThingsSearch.searchTriggered(dom.searchFilterEdit.value, () => null);
111109
}
112110
}
113111

ui/modules/things/thingMessages.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,11 @@ function clearAllFields() {
152152
dom.inputThingMessageTimeout.value = '10';
153153
acePayload.setValue('');
154154
aceResponse.setValue('');
155-
dom.ulThingMessageTemplates.innerHTML = '';
155+
dom.ulThingMessageTemplates.textContent = '';
156156
}
157157

158158
function refillTemplates() {
159-
dom.ulThingMessageTemplates.innerHTML = '';
159+
dom.ulThingMessageTemplates.textContent = '';
160160
Utils.addDropDownEntries(dom.ulThingMessageTemplates, ['Saved message templates'], true);
161161
if (Environments.current().messageTemplates['/']) {
162162
Utils.addDropDownEntries(

ui/modules/things/thingsCRUD.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ function onThingChanged(thingJson) {
124124
updateThingJsonEditor();
125125

126126
function updateThingDetailsTable() {
127-
dom.tbodyThingDetails.innerHTML = '';
127+
dom.tbodyThingDetails.textContent = '';
128128
if (thingJson) {
129129
Utils.addTableRow(dom.tbodyThingDetails, 'thingId', false, true, thingJson.thingId);
130130
Utils.addTableRow(dom.tbodyThingDetails, 'policyId', false, true, thingJson.policyId);

ui/modules/things/thingsSearch.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import { JSONPath } from 'jsonpath-plus';
2020

2121
import * as API from '../api.js';
2222
import * as Environments from '../environments/environments.js';
23-
2423
import * as Utils from '../utils.js';
24+
import { sanitizeHTML } from '../utils.js';
2525
import * as Fields from './fields.js';
2626
import * as Things from './things.js';
2727
import * as ThingsSSE from './thingsSSE.js';
@@ -75,12 +75,14 @@ function onThingsTableClicked(event) {
7575
/**
7676
* Tests if the search filter is an RQL. If yes, things search is called otherwise just things get
7777
* @param {String} filter search filter string containing an RQL or a thingId
78+
* @param rqlFilterCallback a callback to invoke when the passed `filter` was a valid RQL statement
7879
*/
79-
export function searchTriggered(filter) {
80+
export function searchTriggered(filter: string, rqlFilterCallback: () => void) {
8081
lastSearch = filter;
8182
const regex = /^(eq\(|ne\(|gt\(|ge\(|lt\(|le\(|in\(|like\(|ilike\(|exists\(|and\(|or\(|not\().*/;
8283
if (filter === '' || regex.test(filter)) {
8384
searchThings(filter);
85+
rqlFilterCallback();
8486
} else {
8587
getThings([filter]);
8688
}
@@ -104,7 +106,7 @@ export function performLastSearch() {
104106
if (lastSearch === 'pinned') {
105107
pinnedTriggered();
106108
} else {
107-
searchTriggered(lastSearch);
109+
searchTriggered(lastSearch, () => null);
108110
}
109111
}
110112

@@ -113,7 +115,7 @@ export function performLastSearch() {
113115
* @param {Array} thingIds Array of thingIds
114116
*/
115117
export function getThings(thingIds) {
116-
dom.thingsTableBody.innerHTML = '';
118+
dom.thingsTableBody.textContent = '';
117119
const fieldsQueryParameter = Fields.getQueryParameter();
118120
if (thingIds.length > 0) {
119121
API.callDittoREST('GET',
@@ -134,8 +136,8 @@ export function getThings(thingIds) {
134136

135137
function resetAndClearViews(retainThing = false) {
136138
theSearchCursor = null;
137-
dom.thingsTableHead.innerHTML = '';
138-
dom.thingsTableBody.innerHTML = '';
139+
dom.thingsTableHead.textContent = '';
140+
dom.thingsTableBody.textContent = '';
139141
if (!retainThing) {
140142
Things.setTheThing(null);
141143
}
@@ -187,7 +189,7 @@ function searchThings(filter, isMore = false) {
187189

188190
function addMoreToThingList() {
189191
const moreCell = dom.thingsTableBody.insertRow().insertCell(-1);
190-
moreCell.innerHTML = 'load more...';
192+
moreCell.textContent = 'load more...';
191193
moreCell.colSpan = dom.thingsTableBody.rows[0].childElementCount;
192194
moreCell.style.textAlign = 'center';
193195
moreCell.style.cursor = 'pointer';
@@ -225,7 +227,7 @@ function fillThingsTable(thingsList) {
225227
}
226228

227229
function fillHeaderRow() {
228-
dom.thingsTableHead.innerHTML = '';
230+
dom.thingsTableHead.textContent = '';
229231
// Utils.addCheckboxToRow(dom.thingsTableHead, 'checkboxHead', false, null);
230232
Utils.insertHeaderCell(dom.thingsTableHead, '');
231233
Utils.insertHeaderCell(dom.thingsTableHead, 'Thing ID');
@@ -302,7 +304,7 @@ export function updateTableRow(thingUpdateJson) {
302304
path: path,
303305
});
304306
if (elem.length !== 0) {
305-
cell.innerHTML = elem[0];
307+
cell.innerHTML = sanitizeHTML(elem[0]);
306308
}
307309
}
308310
});

0 commit comments

Comments
 (0)