Skip to content

Commit acaa620

Browse files
authored
IBX-9793: Fixed XSS issues in several places
For more details see https://issues.ibexa.co/browse/IBX-9793 Key changes: * IBX-9793: Fixed issues in DOM helper * IBX-9793: Fixed issues in Text helper * IBX-9880: Fixed issues in content type filter in search
1 parent 71819dd commit acaa620

File tree

5 files changed

+54
-7
lines changed

5 files changed

+54
-7
lines changed

src/bundle/Resources/encore/ez.js.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const fieldTypesPath = path.resolve(__dirname, '../public/js/scripts/fieldType/'
55
const layout = [
66
path.resolve(__dirname, '../public/js/scripts/helpers/icon.helper.js'),
77
path.resolve(__dirname, '../public/js/scripts/helpers/text.helper.js'),
8+
path.resolve(__dirname, '../public/js/scripts/helpers/dom.helper.js'),
89
path.resolve(__dirname, '../public/js/scripts/helpers/request.helper.js'),
910
path.resolve(__dirname, '../public/js/scripts/helpers/notification.helper.js'),
1011
path.resolve(__dirname, '../public/js/scripts/helpers/timezone.helper.js'),

src/bundle/Resources/public/js/scripts/admin.search.filters.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
(function (global, doc, eZ, $, flatpickr) {
2+
const { escapeHTML, escapeHTMLAttribute } = eZ.helpers.text;
3+
const { dangerouslySetInnerHTML } = eZ.helpers.dom;
24
let getUsersTimeout;
35
const CLASS_DATE_RANGE = 'ez-filters__range-wrapper';
46
const CLASS_VISIBLE_DATE_RANGE = 'ez-filters__range-wrapper--visible';
@@ -149,11 +151,11 @@
149151
};
150152
const filterByContentType = () => {
151153
const selectedCheckboxes = [...contentTypeCheckboxes].filter((checkbox) => checkbox.checked);
152-
const contentTypesText = selectedCheckboxes.map((checkbox) => checkbox.dataset.name).join(', ');
154+
const contentTypesText = selectedCheckboxes.map((checkbox) => escapeHTML(checkbox.dataset.name)).join(', ');
153155
const option = contentTypeSelect[0];
154156
const defaultText = option.dataset.default;
155157

156-
option.innerHTML = contentTypesText || defaultText;
158+
dangerouslySetInnerHTML(option, contentTypesText || defaultText);
157159

158160
toggleDisabledStateOnApplyBtn();
159161
};
@@ -214,14 +216,17 @@
214216
.then(showUsersList);
215217
};
216218
const createUsersListItem = (user) => {
217-
return `<li data-id="${user._id}" data-name="${user.TranslatedName}" class="ez-filters__user-item">${user.TranslatedName}</li>`;
219+
const userNameHtmlEscaped = escapeHTML(user.TranslatedName);
220+
const userNameHtmlAttributeEscaped = escapeHTMLAttribute(user.TranslatedName);
221+
222+
return `<li data-id="${user._id}" data-name="${userNameHtmlAttributeEscaped}" class="ez-filters__user-item">${userNameHtmlEscaped}</li>`;
218223
};
219224
const showUsersList = (data) => {
220225
const hits = data.View.Result.searchHits.searchHit;
221226
const users = hits.reduce((total, hit) => total + createUsersListItem(hit.value.Content), '');
222227
const methodName = users ? 'addEventListener' : 'removeEventListener';
223228

224-
usersList.innerHTML = users;
229+
dangerouslySetInnerHTML(usersList, users);
225230
usersList.classList.remove('ez-filters__user-list--hidden');
226231

227232
doc.querySelector('body')[methodName]('click', handleClickOutsideUserList, false);

src/bundle/Resources/public/js/scripts/admin.trash.list.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
(function (global, doc, eZ, React, ReactDOM, Translator) {
2+
const { escapeHTML, escapeHTMLAttribute } = eZ.helpers.text;
3+
const { dangerouslySetInnerHTML } = eZ.helpers.dom;
24
let getUsersTimeout;
35
const CLASS_SORTED_ASC = 'ez-table__sort-column--asc';
46
const CLASS_SORTED_DESC = 'ez-table__sort-column--desc';
@@ -149,14 +151,17 @@
149151
.catch(() => eZ.helpers.notification.showErrorNotification(errorMessage));
150152
};
151153
const createUsersListItem = (user) => {
152-
return `<li data-id="${user._id}" data-name="${user.TranslatedName}" class="ez-trash-search-form__user-item">${user.TranslatedName}</li>`;
154+
const userNameHtmlEscaped = escapeHTML(user.TranslatedName);
155+
const userNameHtmlAttributeEscaped = escapeHTMLAttribute(user.TranslatedName);
156+
157+
return `<li data-id="${user._id}" data-name="${userNameHtmlAttributeEscaped}" class="ez-trash-search-form__user-item">${userNameHtmlEscaped}</li>`;
153158
};
154159
const showUsersList = (data) => {
155160
const hits = data.View.Result.searchHits.searchHit;
156161
const users = hits.reduce((total, hit) => total + createUsersListItem(hit.value.Content), '');
157162
const methodName = users ? 'addEventListener' : 'removeEventListener';
158163

159-
usersList.innerHTML = users;
164+
dangerouslySetInnerHTML(usersList, users);
160165
usersList.classList.remove('ez-trash-search-form__user-list--hidden');
161166

162167
doc.querySelector('body')[methodName]('click', handleClickOutsideUserList, false);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
(function (global, doc, eZ) {
2+
const { escapeHTML } = eZ.helpers.text;
3+
const safelySetInnerHTML = (node, text) => {
4+
node.innerHTML = escapeHTML(text);
5+
};
6+
7+
const dangerouslySetInnerHTML = (node, text) => {
8+
node.innerHTML = text;
9+
};
10+
11+
const dangerouslyInsertAdjacentHTML = (node, position, text) => {
12+
const escapedText = text;
13+
14+
node.insertAdjacentHTML(position, escapedText);
15+
};
16+
17+
eZ.addConfig('helpers.dom', {
18+
safelySetInnerHTML,
19+
dangerouslySetInnerHTML,
20+
dangerouslyInsertAdjacentHTML,
21+
});
22+
})(window, window.document, window.eZ);
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
(function(global, doc, eZ) {
1+
(function (global, doc, eZ) {
22
const escapeHTML = (string) => {
33
const stringTempNode = doc.createElement('div');
44

@@ -7,7 +7,21 @@
77
return stringTempNode.innerHTML;
88
};
99

10+
const escapeHTMLAttribute = (string) => {
11+
if (string === null) {
12+
return '';
13+
}
14+
15+
return String(string)
16+
.replace(/&/g, '&amp;')
17+
.replace(/</g, '&lt;')
18+
.replace(/>/g, '&gt;')
19+
.replace(/"/g, '&quot;')
20+
.replace(/'/g, '&#039;');
21+
};
22+
1023
eZ.addConfig('helpers.text', {
1124
escapeHTML,
25+
escapeHTMLAttribute,
1226
});
1327
})(window, window.document, window.eZ);

0 commit comments

Comments
 (0)