CVE-2017-14922: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Stored XSS vulnerability via IMG element at "History" of Profile, Calendar, Tasks, and CRM in Tine 2.0 Community Edition before 2017.08.4 allows an authenticated user to inject JavaScript, which is mishandled during rendering by the application administrator and other users.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2017-09-30T01:29Z
Repository: https://github.com/tine20/Tine-2.0-Open-Source-Groupware-and-CRM
Language: JavaScript
Files changed: tine20/Calendar/js/MainScreenCenterPanel.js, tine20/Filemanager/js/ExceptionHandler.js, tine20/Filemanager/js/GridContextMenu.js, tine20/Filemanager/js/NodeGridPanel.js, tine20/Tinebase/js/MainMenu.js
Commit date: 2017-09-08 18:45:09 +0200
Commit message: node exists exception broken

Change-Id: I50370804af4a80cb360542b13df1621430a6e022

Diff: diff --git a/tine20/Calendar/js/MainScreenCenterPanel.js b/tine20/Calendar/js/MainScreenCenterPanel.js
index 090bdaec4d..e5cd15604a 100644
--- a/tine20/Calendar/js/MainScreenCenterPanel.js
+++ b/tine20/Calendar/js/MainScreenCenterPanel.js
@@ -1545,11 +1545,11 @@ Tine.Calendar.MainScreenCenterPanel = Ext.extend(Ext.Panel, {
                         eventInfo = Date.parseDate(fbInfo.dtstart, Date.patterns.ISO8601Long).format(dateFormat + ' ' + format) + ' - ' + Date.parseDate(fbInfo.dtend, Date.patterns.ISO8601Long).format(dateFormat + ' ' + format);
                     } else {
                         eventInfo = Date.parseDate(fbInfo.dtstart, Date.patterns.ISO8601Long).format(dateFormat + ' ' + format) + ' - ' + Date.parseDate(fbInfo.dtend, Date.patterns.ISO8601Long).format(format);
                     }
                     if (fbInfo.event && fbInfo.event.summary) {
-                        eventInfo += ' : ' + fbInfo.event.summary;
+                        eventInfo += ' : ' + Ext.util.Format.htmlEncode(fbInfo.event.summary);
                     }
                     if (fbInfo.type == 'BUSY_UNAVAILABLE') {
                         denyIgnore = true;
                         eventInfo += '<span class="cal-conflict-eventinfos-unavailable">' + this.app.i18n._('Unavailable') + '</span>';
                     }
diff --git a/tine20/Filemanager/js/ExceptionHandler.js b/tine20/Filemanager/js/ExceptionHandler.js
index b8ff6ec1e5..e08fff6812 100644
--- a/tine20/Filemanager/js/ExceptionHandler.js
+++ b/tine20/Filemanager/js/ExceptionHandler.js
@@ -41,11 +41,11 @@ Tine.Filemanager.handleRequestException = function(exception, request) {
                 Tine.log.debug('Tine.Filemanager.handleRequestException - request exception:');
                 Tine.log.debug(exception);
 
                 if (exception.existingnodesinfo) {
                     for (i = 0; i < exception.existingnodesinfo.length; i++) {
-                        existingFilenames.push(exception.existingnodesinfo[i].name);
+                        existingFilenames.push(Ext.util.Format.htmlEncode(exception.existingnodesinfo[i].name));
                     }
                 }
 
                 this.conflictConfirmWin = Tine.widgets.dialog.FileListDialog.openWindow({
                     modal: true,
diff --git a/tine20/Filemanager/js/GridContextMenu.js b/tine20/Filemanager/js/GridContextMenu.js
index 2ad4cc10d8..4caa6eea6d 100644
--- a/tine20/Filemanager/js/GridContextMenu.js
+++ b/tine20/Filemanager/js/GridContextMenu.js
@@ -123,19 +123,15 @@ Tine.Filemanager.GridContextMenu = {
             
             var nodeName = "";
             if(nodes && nodes.length) {
                 for(var i=0; i<nodes.length; i++) {
                     var currNodeData = nodes[i].data;
-                    
-                    if(typeof currNodeData.name == 'object') {
-                        nodeName += currNodeData.name.name + '<br />';
-                    }
-                    else {
-                        nodeName += currNodeData.name + '<br />';
-                    }
+
+                    nodeName += Ext.util.Format.htmlEncode(typeof currNodeData.name == 'object' ?
+                        currNodeData.name.name :
+                        currNodeData.name) + '<br />';
                 }
-                
             }
             
             this.conflictConfirmWin = Tine.widgets.dialog.FileListDialog.openWindow({
                 modal: true,
                 allowCancel: false,
diff --git a/tine20/Filemanager/js/NodeGridPanel.js b/tine20/Filemanager/js/NodeGridPanel.js
index 095688fb4d..f49acfedcf 100644
--- a/tine20/Filemanager/js/NodeGridPanel.js
+++ b/tine20/Filemanager/js/NodeGridPanel.js
@@ -569,17 +569,14 @@ Tine.Filemanager.NodeGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
             nodes = sm.getSelections();
         
         if(nodes && nodes.length) {
             for(var i=0; i<nodes.length; i++) {
                 var currNodeData = nodes[i].data;
-                
-                if(typeof currNodeData.name == 'object') {
-                    nodeName += currNodeData.name.name + '<br />';
-                }
-                else {
-                    nodeName += currNodeData.name + '<br />';
-                }
+
+                nodeName += Ext.util.Format.htmlEncode(typeof currNodeData.name == 'object' ?
+                    currNodeData.name.name :
+                    currNodeData.name) + '<br />';
             }
         }
         
         this.conflictConfirmWin = Tine.widgets.dialog.FileListDialog.openWindow({
             modal: true,
diff --git a/tine20/Tinebase/js/MainMenu.js b/tine20/Tinebase/js/MainMenu.js
index 4c6a0dbdbf..1b49a756b6 100644
--- a/tine20/Tinebase/js/MainMenu.js
+++ b/tine20/Tinebase/js/MainMenu.js
@@ -61,11 +61,11 @@ Tine.Tinebase.MainMenu = Ext.extend(Ext.Toolbar, {
         '->',
         this.actionLearnMore,
         // TODO add a bigger spacer here?
         { xtype: 'spacer' },
         {
-            text: String.format(i18n._('User: {0}'), Tine.Tinebase.registry.get('currentAccount').accountDisplayName),
+            text: Ext.util.Format.htmlEncode(String.format(i18n._('User: {0}'), Tine.Tinebase.registry.get('currentAccount').accountDisplayName)),
             menu: this.getUserActions(),
             menuAlign: 'tr-br',
             iconCls: Tine.Tinebase.registry.get('userAccountChanged') ? 'renderer_accountUserChangedIcon' : 'renderer_accountUserIcon'
         },
         this.onlineStatus, 

Vulnerable code:                         eventInfo += ' : ' + fbInfo.event.summary;
                        existingFilenames.push(exception.existingnodesinfo[i].name);
                    
                    if(typeof currNodeData.name == 'object') {
                        nodeName += currNodeData.name.name + '<br />';
                    }
                    else {
                        nodeName += currNodeData.name + '<br />';
                    }
                
                
                if(typeof currNodeData.name == 'object') {
                    nodeName += currNodeData.name.name + '<br />';
                }
                else {
                    nodeName += currNodeData.name + '<br />';
                }
            text: String.format(i18n._('User: {0}'), Tine.Tinebase.registry.get('currentAccount').accountDisplayName),
Fixed code:                         eventInfo += ' : ' + Ext.util.Format.htmlEncode(fbInfo.event.summary);
                        existingFilenames.push(Ext.util.Format.htmlEncode(exception.existingnodesinfo[i].name));

                    nodeName += Ext.util.Format.htmlEncode(typeof currNodeData.name == 'object' ?
                        currNodeData.name.name :
                        currNodeData.name) + '<br />';

                nodeName += Ext.util.Format.htmlEncode(typeof currNodeData.name == 'object' ?
                    currNodeData.name.name :
                    currNodeData.name) + '<br />';
            text: Ext.util.Format.htmlEncode(String.format(i18n._('User: {0}'), Tine.Tinebase.registry.get('currentAccount').accountDisplayName)),