diff --git a/dotCMS/src/main/webapp/html/portlet/ext/useradmin/view_users_js_inc.jsp b/dotCMS/src/main/webapp/html/portlet/ext/useradmin/view_users_js_inc.jsp index 5352e19397c..2638d4f13c8 100644 --- a/dotCMS/src/main/webapp/html/portlet/ext/useradmin/view_users_js_inc.jsp +++ b/dotCMS/src/main/webapp/html/portlet/ext/useradmin/view_users_js_inc.jsp @@ -171,9 +171,21 @@ }; var usersStore = new dojo.data.ItemFileReadStore({data: usersData}); + // HTML-escape user-controlled grid values so a name/email containing markup + // renders as text instead of executing (stored XSS). See dotCMS/private-issues#651. + var escapeGridHtml = function (value) { + if (value === null || value === undefined) { return ""; } + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + }; + var usersGridLayout = [[ - { name: nameColumn, field: 'name', width:'50%' }, - { name: emailColumn, field: 'email', width:'50%' }, + { name: nameColumn, field: 'name', width:'50%', formatter: escapeGridHtml }, + { name: emailColumn, field: 'email', width:'50%', formatter: escapeGridHtml }, ]]; //Initialization kicking the loading of users @@ -418,7 +430,7 @@ dijit.byId('userId').attr('value', user.id); dijit.byId('userId').setDisabled(true); } else { - dojo.byId('userIdValue').innerHTML = user.id; + dojo.byId('userIdValue').textContent = user.id; dojo.byId('userId').value = user.id; } @@ -457,7 +469,9 @@ dijit.byId('password').attr('value', '********'); dijit.byId('passwordCheck').attr('value', '********'); - dojo.query(".fullUserName").forEach(function (elem) { elem.innerHTML = user.name; }); + // Render the user's name as text, never as HTML, to prevent stored XSS + // (first/last name are user-controlled). See dotCMS/private-issues#651. + dojo.query(".fullUserName").forEach(function (elem) { elem.textContent = user.name; }); userChanged = false; newUser = false;