CVE-2022-1931: Improper Synchronization (CWE-662)
value: Incorrect Synchronization in GitHub repository polonel/trudesk prior to 1.2.3.
CWE description: The product utilizes multiple threads or processes to allow temporary access to a shared resource that can only be exclusive to one process at a time, but it does not properly synchronize these actions, which might cause simultaneous accesses of this resource by multiple threads or processes.
CVSS v3 base score: 8.1
Published: 2022-05-31T02:15Z
Repository: https://github.com/polonel/trudesk
Language: JavaScript
Files changed: src/client/containers/Tickets/TicketsContainer.jsx, src/controllers/api/v1/messages.js, src/public/js/pages/messages.js
Commit date: 2022-05-28 19:59:21 -0400
Commit message: fix(messages): validation check
Diff: diff --git a/src/client/containers/Tickets/TicketsContainer.jsx b/src/client/containers/Tickets/TicketsContainer.jsx
index a555afca4..5536ede7c 100644
--- a/src/client/containers/Tickets/TicketsContainer.jsx
+++ b/src/client/containers/Tickets/TicketsContainer.jsx
@@ -102,11 +102,10 @@ class TicketsContainer extends React.Component {
   onTicketCreated (ticket) {
     if (this.props.page === '0') this.props.ticketEvent({ type: 'created', data: ticket })
   }
 
   onTicketUpdated (data) {
-    console.log(data)
     this.props.ticketUpdated(data)
   }
 
   onTicketDeleted (id) {
     this.props.ticketEvent({ type: 'deleted', data: id })
diff --git a/src/controllers/api/v1/messages.js b/src/controllers/api/v1/messages.js
index b754d4a75..4db4d20bb 100644
--- a/src/controllers/api/v1/messages.js
+++ b/src/controllers/api/v1/messages.js
@@ -12,13 +12,14 @@
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 var async = require('async')
 var _ = require('lodash')
-var winston = require('winston')
+var winston = require('../../../logger')
 var ConversationSchema = require('../../../models/chat/conversation')
 var MessageSchema = require('../../../models/chat/message')
+var UserSchema = require('../../../models/user')
 
 var apiMessages = {}
 
 /**
  * @api {get} /api/v1/messages Get Messages
@@ -189,26 +190,36 @@ apiMessages.send = function (req, res) {
   }
 
   async.waterfall(
     [
       function (done) {
+        ConversationSchema.findOne({ _id: cId }, function (err, convo) {
+          if (err || !convo) return done('Invalid Conversation')
+
+          return done(null, convo)
+        })
+      },
+      function (convo, done) {
         // Updated conversation to save UpdatedAt field.
-        ConversationSchema.findOneAndUpdate({ _id: cId }, { updatedAt: new Date() }, { new: false }, function (
-          err,
-          convo
-        ) {
+        convo.updatedAt = new Date()
+        convo.save(function (err, savedConvo) {
           if (err) return done(err)
-          if (convo === null || convo === undefined) {
-            return done('Invalid Conversation: ' + convo)
-          }
-          return done(null, convo)
+
+          return done(null, savedConvo)
         })
       },
       function (convo, done) {
-        var Message = new MessageSchema({
+        UserSchema.findOne({ _id: owner }, function (err, user) {
+          if (err || !user) return done('Invalid Conversation')
+
+          return done(null, user, convo)
+        })
+      },
+      function (user, convo, done) {
+        const Message = new MessageSchema({
           conversation: convo._id,
-          owner: owner,
+          owner: user._id,
           body: message
         })
 
         Message.save(function (err, mSave) {
           if (err) {
diff --git a/src/public/js/pages/messages.js b/src/public/js/pages/messages.js
index 84c2bf4f0..089a706b0 100644
--- a/src/public/js/pages/messages.js
+++ b/src/public/js/pages/messages.js
@@ -21,45 +21,38 @@ define('pages/messages', [
   'modules/helpers',
   'modules/socket',
   'history',
   'isinview'
 ], function ($, _, angular, UIKit, moment, helpers) {
-  var messagesPage = {}
+  const messagesPage = {}
 
   messagesPage.init = function (callback) {
     $(document).ready(function () {
-      var $messageScroller = $('#message-content.scrollable')
-
-      var $messagesWrapper = $('#messages')
-
-      var $scrollspy = $('#conversation-scrollspy')
-
-      var $spinner = $scrollspy.find('i')
-
-      var $searchBox = $('.search-box').find('input')
-
-      var $nextPage = 2
-
-      var $enabled = true
-
-      var $loading = false
-
+      const $messageScroller = $('#message-content.scrollable')
+      const $messagesWrapper = $('#messages')
+      const $scrollspy = $('#conversation-scrollspy')
+      const $spinner = $scrollspy.find('i')
+      const $searchBox = $('.search-box').find('input')
+      let $nextPage = 2
+      let $enabled = true
+      let $loading = false
       // $inview             = null,
+      const $recentMessages = {}
+      const $convoId = $('#message-content[data-conversation-id]').attr('data-conversation-id')
+      const $loggedInAccountId = window.trudeskSessionService.getUser()._id
 
-      var $recentMessages = {}
-
-      var $convoId = $('#message-content[data-conversation-id]').attr('data-conversation-id')
-
-      var $loggedInAccountId = window.trudeskSessionService.getUser()._id
+      setTimeout(function () {
+        $('script#preloader').remove()
+      }, 1000)
 
       // Setup Context Menu
       helpers.setupContextMenu('#convo-list > ul > li', function (action, target) {
-        var $li = $(target)
+        let $li = $(target)
         if (!$li.is('li')) {
           $li = $(target).parents('li')
         }
-        var convoId = $li.attr('data-conversation-id')
+        const convoId = $li.attr('data-conversation-id')
         if (action.toLowerCase() === 'delete') {
           UIKit.modal.confirm(
             'Are you sure you want to delete this conversation?',
             function () {
               // Confirm
@@ -85,32 +78,32 @@ define('pages/messages', [
       $(window).on('$trudesk:ready.messages', function () {
         helpers.scrollToBottom($messageScroller)
 
         // set active
         if ($convoId !== undefined) {
-          var item = $('ul > li[data-conversation-id="' + $convoId + '"]')
+          const item = $('ul > li[data-conversation-id="' + $convoId + '"]')
           item.addClass('active')
         }
 
         // Remove All Chat Boxes
         if (
           $('#__page')
             .text()
             .toLowerCase() === 'messages'
         ) {
           $('.chat-box-position').each(function () {
-            var self = $(this)
+            const self = $(this)
             self.remove()
           })
 
           $('.message-textbox')
             .find('input')
             .focus()
 
           $messageScroller.scroll(function () {
             if ($scrollspy.isInView($messageScroller)) {
-              var run = _.throttle(loadMoreMessages, 100)
+              const run = _.throttle(loadMoreMessages, 100)
               run()
             }
           })
         }
       })
@@ -120,15 +113,15 @@ define('pages/messages', [
           url: '/api/v1/messages/conversation/' + convoId,
           method: 'DELETE',
           success: function (response) {
             if (response.success) {
               // Check if on conversation
-              var $convo = $('#message-content[data-conversation-id="' + response.conversation._id + '"]')
+              const $convo = $('#message-content[data-conversation-id="' + response.conversation._id + '"]')
               if ($convo.length > 0) {
                 History.pushState(null, null, '/messages', false)
               } else {
-                var $convoLI = $('#convo-list').find('li[data-conversation-id="' + response.conversation._id + '"]')
+                const $convoLI = $('#convo-list').find('li[data-conversation-id="' + response.conversation._id + '"]')
                 if ($convoLI.length > 0) {
                   $convoLI.remove()
                 }
               }
 
@@ -144,11 +137,11 @@ define('pages/messages', [
           }
         })
       }
 
       function onSearchKeyUp () {
-        var searchTerm = $searchBox.val().toLowerCase()
+        const searchTerm = $searchBox.val().toLowerCase()
         $('.all-user-list li').each(function () {
           if ($(this).filter('[data-search-term *= ' + searchTerm + ']').length > 0 || searchTerm.length < 1) {
             $(this).show()
           } else {
             $(this).hide()
@@ -166,36 +159,36 @@ define('pages/messages', [
         $.ajax({
           url: '/api/v1/messages/conversation/' + $convoId + '?page=' + $nextPage
         })
           .done(function (data) {
             $spinner.addClass('uk-hidden')
-            var messages = data.messages
+            const messages = data.messages
             if (_.size(messages) < 1) {
               $enabled = false
               $loading = false
               return false
             }
 
-            var html = ''
+            let html = ''
 
             _.each(messages, function (m) {
-              var h = buildMessageHTML(m)
+              const h = buildMessageHTML(m)
               if (h.length > 0) html += h
             })
 
-            var stage = $('<div></div>')
+            const stage = $('<div></div>')
               .appendTo('body')
               .addClass('stage')
               .css({
                 opacity: 0,
                 visibility: 'hidden',
                 position: 'absolute',
                 top: '-9999em',
                 left: '-9999em'
               })
               .append(html)
-            var height = $(stage).outerHeight()
+            const height = $(stage).outerHeight()
             $(stage).remove()
 
             $messagesWrapper.prepend(html)
 
             UIKit.$html.trigger('changed.uk.dom')
@@ -208,19 +201,19 @@ define('pages/messages', [
             console.log(err)
           })
       }
 
       function buildMessageHTML (message) {
-        var html = ''
-        var loggedInAccountId = window.trudeskSessionService.getUser()._id
+        let html = ''
+        const loggedInAccountId = window.trudeskSessionService.getUser()._id
         if (loggedInAccountId === undefined) return false
-        var left = true
+        let left = true
         if (message.owner._id.toString() === loggedInAccountId.toString()) {
           left = false
         }
 
-        var image = message.owner.image === undefined ? 'defaultProfile.jpg' : message.owner.image
+        const image = message.owner.image === undefined ? 'defaultProfile.jpg' : message.owner.image
 
         if (left) {
           html += '<div class="message message-left">'
           html +=
             '<img class="profileImage" src="/uploads/users/' +
@@ -257,84 +250,84 @@ define('pages/messages', [
       // Remove all Events in the .conversation namespace for this page.
       $(window).off('.conversation')
 
       // On user Typing
       $(window).on('$trudesk:chat:typing.conversation', function (event, data) {
-        var convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
+        const convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
         if (convoListItem.length > 0) {
           $recentMessages[data.cid] = convoListItem.find('.message-subject').text()
           convoListItem.find('.message-subject').text(data.fromUser.fullname + ' is typing...')
         }
       })
 
       $(window).on('$trudesk:chat:stoptyping.conversation', function (event, data) {
-        var convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
+        const convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
         if (convoListItem.length > 0) {
           convoListItem.find('.message-subject').text($recentMessages[data.cid])
         }
       })
 
       // On Chat Message
       $(window).on('$trudesk:chat:message.conversation', function (event, data) {
-        var message = {
+        const message = {
           _id: data.messageId,
           conversation: data.conversation,
           body: data.message,
           owner: data.fromUser
         }
 
-        var html = buildMessageHTML(message)
-        var messageWrapper = $('#message-content[data-conversation-id="' + message.conversation + '"]')
+        const html = buildMessageHTML(message)
+        const messageWrapper = $('#message-content[data-conversation-id="' + message.conversation + '"]')
         if (messageWrapper.length > 0) {
           messageWrapper.find('#messages').append(html)
         }
 
-        var convoListItem = $('li[data-conversation-id="' + data.conversation + '"]')
+        const convoListItem = $('li[data-conversation-id="' + data.conversation + '"]')
         if (convoListItem.length > 0) {
           convoListItem.attr('data-updatedAt', new Date())
-          var ul = convoListItem.parent('ul')
-          var li = ul.children('li')
+          const ul = convoListItem.parent('ul')
+          const li = ul.children('li')
           li.detach().sort(function (a, b) {
             return new Date($(a).attr('data-updatedAt')) < new Date($(b).attr('data-updatedAt'))
           })
 
           ul.append(li)
 
-          var fromName = message.owner.fullname
+          let fromName = message.owner.fullname
           if (message.owner._id.toString() === $loggedInAccountId) {
             fromName = 'You'
           }
 
           convoListItem.find('.message-subject').text(fromName + ': ' + message.body)
           $recentMessages[message.conversation] = fromName + ': ' + message.body
-          var timezone = helpers.getTimezone()
+          const timezone = helpers.getTimezone()
           convoListItem.find('.message-date').text(
             moment
               .utc()
               .tz(timezone)
               .calendar()
           )
         } else {
-          var convoUL = $('#convo-list > ul.message-items')
+          const convoUL = $('#convo-list > ul.message-items')
           if (convoUL.length > 0) {
-            var partner = message.owner
+            let partner = message.owner
             if (message.owner._id.toString() === $loggedInAccountId.toString()) {
               partner = data.toUser
             }
-            var newLI = buildConversationListItem({
+            const newLI = buildConversationListItem({
               _id: message.conversation,
               partner: partner,
               updatedAt: new Date(),
               recentMessage: message.owner.fullname + ': ' + message.body
             })
 
-            var $injector = angular.injector(['ng', 'trudesk'])
+            const $injector = angular.injector(['ng', 'trudesk'])
             $injector.invoke([
               '$compile',
               '$rootScope',
               function ($compile, $rootScope) {
-                var $scope = convoUL.prepend(newLI).scope()
+                const $scope = convoUL.prepend(newLI).scope()
                 $compile(convoUL)($scope || $rootScope)
                 $rootScope.$digest()
               }
             ])
           }
@@ -343,22 +336,22 @@ define('pages/messages', [
         UIKit.$html.trigger('changed.uk.dom')
         helpers.scrollToBottom($messageScroller)
       })
 
       function buildConversationListItem (data) {
-        var html = ''
+        let html = ''
 
         html +=
           '<li ng-click="loadConversation(\'' +
           data._id +
           '\');" data-conversation-id="' +
           data._id +
           '" data-updatedAt="' +
           data.updatedAt +
           '">'
         html += '<div class="profile-pic">'
-        var imageUrl = 'defaultProfile.jpg'
+        let imageUrl = 'defaultProfile.jpg'
         if (data.partner.image) {
           imageUrl = data.partner.image
         }
         html +=
           '<img src="/uploads/users/' +

Vulnerable code:     console.log(data)
var winston = require('winston')
        ConversationSchema.findOneAndUpdate({ _id: cId }, { updatedAt: new Date() }, { new: false }, function (
          err,
          convo
        ) {
          if (convo === null || convo === undefined) {
            return done('Invalid Conversation: ' + convo)
          }
          return done(null, convo)
        var Message = new MessageSchema({
          owner: owner,
  var messagesPage = {}
      var $messageScroller = $('#message-content.scrollable')

      var $messagesWrapper = $('#messages')

      var $scrollspy = $('#conversation-scrollspy')

      var $spinner = $scrollspy.find('i')

      var $searchBox = $('.search-box').find('input')

      var $nextPage = 2

      var $enabled = true

      var $loading = false

      var $recentMessages = {}

      var $convoId = $('#message-content[data-conversation-id]').attr('data-conversation-id')

      var $loggedInAccountId = window.trudeskSessionService.getUser()._id
        var $li = $(target)
        var convoId = $li.attr('data-conversation-id')
          var item = $('ul > li[data-conversation-id="' + $convoId + '"]')
            var self = $(this)
              var run = _.throttle(loadMoreMessages, 100)
              var $convo = $('#message-content[data-conversation-id="' + response.conversation._id + '"]')
                var $convoLI = $('#convo-list').find('li[data-conversation-id="' + response.conversation._id + '"]')
        var searchTerm = $searchBox.val().toLowerCase()
            var messages = data.messages
            var html = ''
              var h = buildMessageHTML(m)
            var stage = $('<div></div>')
            var height = $(stage).outerHeight()
        var html = ''
        var loggedInAccountId = window.trudeskSessionService.getUser()._id
        var left = true
        var image = message.owner.image === undefined ? 'defaultProfile.jpg' : message.owner.image
        var convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
        var convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
        var message = {
        var html = buildMessageHTML(message)
        var messageWrapper = $('#message-content[data-conversation-id="' + message.conversation + '"]')
        var convoListItem = $('li[data-conversation-id="' + data.conversation + '"]')
          var ul = convoListItem.parent('ul')
          var li = ul.children('li')
          var fromName = message.owner.fullname
          var timezone = helpers.getTimezone()
          var convoUL = $('#convo-list > ul.message-items')
            var partner = message.owner
            var newLI = buildConversationListItem({
            var $injector = angular.injector(['ng', 'trudesk'])
                var $scope = convoUL.prepend(newLI).scope()
        var html = ''
        var imageUrl = 'defaultProfile.jpg'
Fixed code: var winston = require('../../../logger')
var UserSchema = require('../../../models/user')
        ConversationSchema.findOne({ _id: cId }, function (err, convo) {
          if (err || !convo) return done('Invalid Conversation')

          return done(null, convo)
        })
      },
      function (convo, done) {
        convo.updatedAt = new Date()
        convo.save(function (err, savedConvo) {

          return done(null, savedConvo)
        UserSchema.findOne({ _id: owner }, function (err, user) {
          if (err || !user) return done('Invalid Conversation')

          return done(null, user, convo)
        })
      },
      function (user, convo, done) {
        const Message = new MessageSchema({
          owner: user._id,
  const messagesPage = {}
      const $messageScroller = $('#message-content.scrollable')
      const $messagesWrapper = $('#messages')
      const $scrollspy = $('#conversation-scrollspy')
      const $spinner = $scrollspy.find('i')
      const $searchBox = $('.search-box').find('input')
      let $nextPage = 2
      let $enabled = true
      let $loading = false
      const $recentMessages = {}
      const $convoId = $('#message-content[data-conversation-id]').attr('data-conversation-id')
      const $loggedInAccountId = window.trudeskSessionService.getUser()._id
      setTimeout(function () {
        $('script#preloader').remove()
      }, 1000)
        let $li = $(target)
        const convoId = $li.attr('data-conversation-id')
          const item = $('ul > li[data-conversation-id="' + $convoId + '"]')
            const self = $(this)
              const run = _.throttle(loadMoreMessages, 100)
              const $convo = $('#message-content[data-conversation-id="' + response.conversation._id + '"]')
                const $convoLI = $('#convo-list').find('li[data-conversation-id="' + response.conversation._id + '"]')
        const searchTerm = $searchBox.val().toLowerCase()
            const messages = data.messages
            let html = ''
              const h = buildMessageHTML(m)
            const stage = $('<div></div>')
            const height = $(stage).outerHeight()
        let html = ''
        const loggedInAccountId = window.trudeskSessionService.getUser()._id
        let left = true
        const image = message.owner.image === undefined ? 'defaultProfile.jpg' : message.owner.image
        const convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
        const convoListItem = $('#convo-list').find('li[data-conversation-id="' + data.cid + '"]')
        const message = {
        const html = buildMessageHTML(message)
        const messageWrapper = $('#message-content[data-conversation-id="' + message.conversation + '"]')
        const convoListItem = $('li[data-conversation-id="' + data.conversation + '"]')
          const ul = convoListItem.parent('ul')
          const li = ul.children('li')
          let fromName = message.owner.fullname
          const timezone = helpers.getTimezone()
          const convoUL = $('#convo-list > ul.message-items')
            let partner = message.owner
            const newLI = buildConversationListItem({
            const $injector = angular.injector(['ng', 'trudesk'])
                const $scope = convoUL.prepend(newLI).scope()
        let html = ''
        let imageUrl = 'defaultProfile.jpg'