CVE-2022-1290: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Stored XSS in "Name", "Group Name" & "Title" in GitHub repository polonel/trudesk prior to v1.2.0. This allows attackers to execute malicious scripts in the user's browser and it can lead to session hijacking, sensitive data exposure, and worse.
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: 2022-04-10T16:15Z
Repository: https://github.com/polonel/trudesk
Language: JavaScript
Files changed: src/helpers/utils/index.js, src/models/attachment.js, src/models/department.js, src/models/group.js, src/models/notice.js, src/models/role.js, src/models/tag.js, src/models/team.js, src/models/ticket.js, src/models/ticketpriority.js, src/models/tickettype.js, src/models/user.js
Commit date: 2022-03-31 18:04:46 -0400
Commit message: chore(security): fix issue where html was allowed in some input fields
Diff: diff --git a/src/helpers/utils/index.js b/src/helpers/utils/index.js
index 1c723125..7fdd3346 100644
--- a/src/helpers/utils/index.js
+++ b/src/helpers/utils/index.js
@@ -10,11 +10,21 @@
  *  Author:     Chris Brame
  *  Updated:    1/20/19 4:43 PM
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
-var _ = require('lodash')
+const _ = require('lodash')
+const xss = require('xss')
+
+module.exports.sanitizeFieldPlainText = function (text) {
+  const t = xss(text, {
+    whileList: {},
+    stripIgnoreTag: true,
+    stripIgnoreTagBody: ['script']
+  })
+  return t
+}
 
 module.exports.sendToSelf = function (socket, method, data) {
   socket.emit(method, data)
 }
 
diff --git a/src/models/attachment.js b/src/models/attachment.js
index bf081039..cd30bca4 100644
--- a/src/models/attachment.js
+++ b/src/models/attachment.js
@@ -11,21 +11,22 @@
  *  Updated:    1/20/19 4:43 PM
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 var attachmentSchema = mongoose.Schema({
   owner: { type: mongoose.Schema.Types.ObjectId, ref: 'accounts' },
   name: { type: String, required: true },
   date: { type: Date, required: true, default: Date.now },
   path: { type: String, required: true },
   type: { type: String, required: true }
 })
 
 attachmentSchema.pre('save', function (next) {
-  this.name = this.name.trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
 
   return next()
 })
 
 module.exports = attachmentSchema
diff --git a/src/models/department.js b/src/models/department.js
index f7d0118b..1424208b 100644
--- a/src/models/department.js
+++ b/src/models/department.js
@@ -13,10 +13,11 @@
  **/
 
 var _ = require('lodash')
 var async = require('async')
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 // Refs
 require('./group')
 var Teams = require('./team')
 var Groups = require('./group')
@@ -33,12 +34,12 @@ var departmentSchema = mongoose.Schema({
 })
 
 departmentSchema.plugin(require('mongoose-autopopulate'))
 
 departmentSchema.pre('save', function (next) {
-  this.name = this.name.trim()
-  this.normalized = this.name.trim().toLowerCase()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
+  this.normalized = utils.sanitizeFieldPlainText(this.name.trim().toLowerCase())
 
   return next()
 })
 
 departmentSchema.statics.getDepartmentsByTeam = function (teamIds, callback) {
diff --git a/src/models/group.js b/src/models/group.js
index 53414b82..8207b137 100644
--- a/src/models/group.js
+++ b/src/models/group.js
@@ -12,10 +12,11 @@
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 var _ = require('lodash')
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 var COLLECTION = 'groups'
 
 /**
  * Group Schema
@@ -42,11 +43,11 @@ var groupSchema = mongoose.Schema({
 })
 
 groupSchema.plugin(require('mongoose-autopopulate'))
 
 groupSchema.pre('save', function (next) {
-  this.name = this.name.trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
 
   next()
 })
 
 groupSchema.methods.addMember = function (memberId, callback) {
diff --git a/src/models/notice.js b/src/models/notice.js
index 3fb011f6..590fb361 100644
--- a/src/models/notice.js
+++ b/src/models/notice.js
@@ -8,10 +8,11 @@
    "888" d888b     `V88V"V8P' `Y8bod88P" `Y8bod8P' 8""888P' o888o o888o
  ========================================================================
  */
 
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 var COLLECTION = 'notices'
 
 /**
  * @since 1.0
@@ -39,11 +40,12 @@ var noticeSchema = mongoose.Schema({
   activeDate: { type: Date, default: Date.now },
   alertWindow: { type: Boolean, default: false }
 })
 
 noticeSchema.pre('save', function (next) {
-  this.name = this.name.trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
+  this.message = utils.sanitizeFieldPlainText(this.message.trim())
 
   return next()
 })
 
 noticeSchema.statics.getNotices = function (callback) {
diff --git a/src/models/role.js b/src/models/role.js
index e14c9d15..5db23816 100644
--- a/src/models/role.js
+++ b/src/models/role.js
@@ -13,10 +13,11 @@
  **/
 
 var mongoose = require('mongoose')
 var mongooseLeanVirtuals = require('mongoose-lean-virtuals')
 var _ = require('lodash')
+var utils = require('../helpers/utils')
 
 var COLLECTION = 'roles'
 
 var roleSchema = mongoose.Schema(
   {
@@ -49,12 +50,12 @@ roleSchema.virtual('isAgent').get(function () {
 })
 
 roleSchema.plugin(mongooseLeanVirtuals)
 
 roleSchema.pre('save', function (next) {
-  this.name = this.name.trim()
-  this.normalized = this.name.toLowerCase().trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
+  this.normalized = utils.sanitizeFieldPlainText(this.name.toLowerCase().trim())
 
   return next()
 })
 
 roleSchema.methods.updateGrants = function (grants, callback) {
diff --git a/src/models/tag.js b/src/models/tag.js
index 4f9c22f4..f4c415cd 100644
--- a/src/models/tag.js
+++ b/src/models/tag.js
@@ -11,10 +11,11 @@
  *  Updated:    1/20/19 4:43 PM
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 var COLLECTION = 'tags'
 
 /**
  * Tag Schema
@@ -29,12 +30,12 @@ var tagSchema = mongoose.Schema({
   name: { type: String, required: true, unique: true },
   normalized: String
 })
 
 tagSchema.pre('save', function (next) {
-  this.name = this.name.trim()
-  this.normalized = this.name.toLowerCase().trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
+  this.normalized = utils.sanitizeFieldPlainText(this.name.toLowerCase().trim())
 
   return next()
 })
 
 tagSchema.statics.getTag = function (id, callback) {
diff --git a/src/models/team.js b/src/models/team.js
index 511a1c34..8c56349c 100644
--- a/src/models/team.js
+++ b/src/models/team.js
@@ -12,10 +12,11 @@
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 var _ = require('lodash')
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 // Refs
 require('./user')
 
 var COLLECTION = 'teams'
@@ -33,15 +34,15 @@ var teamSchema = mongoose.Schema({
 })
 
 teamSchema.plugin(require('mongoose-autopopulate'))
 
 teamSchema.pre('validate', function () {
-  this.normalized = this.name.trim().toLowerCase()
+  this.normalized = utils.sanitizeFieldPlainText(this.name.trim().toLowerCase())
 })
 
 teamSchema.pre('save', function (next) {
-  this.name = this.name.trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
 
   return next()
 })
 
 teamSchema.methods.addMember = function (memberId, callback) {
diff --git a/src/models/ticket.js b/src/models/ticket.js
index 97a84e72..9f236038 100644
--- a/src/models/ticket.js
+++ b/src/models/ticket.js
@@ -18,10 +18,11 @@ var winston = require('winston')
 var _ = require('lodash')
 var moment = require('moment')
 var sanitizeHtml = require('sanitize-html')
 // var redisCache          = require('../cache/rediscache');
 var xss = require('xss')
+var utils = require('../helpers/utils')
 
 // Needed - For Population
 var groupSchema = require('./group')
 require('./tickettype')
 var userSchema = require('./user')
@@ -118,11 +119,11 @@ var autoPopulate = function (next) {
 }
 
 ticketSchema.pre('findOne', autoPopulate).pre('find', autoPopulate)
 
 ticketSchema.pre('save', function (next) {
-  this.subject = this.subject.trim()
+  this.subject = utils.sanitizeFieldPlainText(this.subject.trim())
   this.wasNew = this.isNew
 
   if (!_.isUndefined(this.uid) || this.uid) {
     return next()
   }
diff --git a/src/models/ticketpriority.js b/src/models/ticketpriority.js
index fdea395d..a05955f2 100644
--- a/src/models/ticketpriority.js
+++ b/src/models/ticketpriority.js
@@ -14,10 +14,11 @@
 
 // var _               = require('lodash');
 var mongoose = require('mongoose')
 var moment = require('moment')
 require('moment-duration-format')
+var utils = require('../helpers/utils')
 
 var COLLECTION = 'priorities'
 
 var prioritySchema = mongoose.Schema(
   {
@@ -34,11 +35,11 @@ var prioritySchema = mongoose.Schema(
     }
   }
 )
 
 prioritySchema.pre('save', function (next) {
-  this.name = this.name.trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
 
   return next()
 })
 
 prioritySchema.virtual('durationFormatted').get(function () {
diff --git a/src/models/tickettype.js b/src/models/tickettype.js
index a0158459..c1925ff1 100644
--- a/src/models/tickettype.js
+++ b/src/models/tickettype.js
@@ -12,10 +12,11 @@
  *  Copyright (c) 2014-2019. All rights reserved.
  */
 
 var _ = require('lodash')
 var mongoose = require('mongoose')
+var utils = require('../helpers/utils')
 
 var COLLECTION = 'tickettypes'
 
 // Needed for Population
 require('./ticketpriority')
@@ -41,11 +42,11 @@ var autoPopulatePriorities = function (next) {
 
 ticketTypeSchema.pre('find', autoPopulatePriorities)
 ticketTypeSchema.pre('findOne', autoPopulatePriorities)
 
 ticketTypeSchema.pre('save', function (next) {
-  this.name = this.name.trim()
+  this.name = utils.sanitizeFieldPlainText(this.name.trim())
 
   return next()
 })
 
 /**
diff --git a/src/models/user.js b/src/models/user.js
index 1a531de2..894e681a 100644
--- a/src/models/user.js
+++ b/src/models/user.js
@@ -16,10 +16,11 @@ var async = require('async')
 var mongoose = require('mongoose')
 var winston = require('winston')
 var bcrypt = require('bcrypt')
 var _ = require('lodash')
 var Chance = require('chance')
+const utils = require('../helpers/utils')
 
 // Required for linkage
 require('./role')
 
 var SALT_FACTOR = 10
@@ -87,14 +88,15 @@ var autoPopulateRole = function (next) {
 userSchema.pre('findOne', autoPopulateRole).pre('find', autoPopulateRole)
 
 userSchema.pre('save', function (next) {
   var user = this
 
-  user.username = user.username.toLowerCase().trim()
-  user.email = user.email.trim()
-  if (user.fullname) user.fullname = user.fullname.trim()
-  if (user.title) user.title = user.title.trim()
+  user.username = utils.sanitizeFieldPlainText(user.username.toLowerCase().trim())
+  user.email = utils.sanitizeFieldPlainText(user.email.trim())
+
+  if (user.fullname) user.fullname = utils.sanitizeFieldPlainText(user.fullname.trim())
+  if (user.title) user.title = utils.sanitizeFieldPlainText(user.title.trim())
 
   if (!user.isModified('password')) {
     return next()
   }
 

Vulnerable code: var _ = require('lodash')
  this.name = this.name.trim()
  this.name = this.name.trim()
  this.normalized = this.name.trim().toLowerCase()
  this.name = this.name.trim()
  this.name = this.name.trim()
  this.name = this.name.trim()
  this.normalized = this.name.toLowerCase().trim()
  this.name = this.name.trim()
  this.normalized = this.name.toLowerCase().trim()
  this.normalized = this.name.trim().toLowerCase()
  this.name = this.name.trim()
  this.subject = this.subject.trim()
  this.name = this.name.trim()
  this.name = this.name.trim()
  user.username = user.username.toLowerCase().trim()
  user.email = user.email.trim()
  if (user.fullname) user.fullname = user.fullname.trim()
  if (user.title) user.title = user.title.trim()
Fixed code: const _ = require('lodash')
const xss = require('xss')

module.exports.sanitizeFieldPlainText = function (text) {
  const t = xss(text, {
    whileList: {},
    stripIgnoreTag: true,
    stripIgnoreTagBody: ['script']
  })
  return t
}
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
  this.normalized = utils.sanitizeFieldPlainText(this.name.trim().toLowerCase())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
  this.message = utils.sanitizeFieldPlainText(this.message.trim())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
  this.normalized = utils.sanitizeFieldPlainText(this.name.toLowerCase().trim())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
  this.normalized = utils.sanitizeFieldPlainText(this.name.toLowerCase().trim())
var utils = require('../helpers/utils')
  this.normalized = utils.sanitizeFieldPlainText(this.name.trim().toLowerCase())
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
var utils = require('../helpers/utils')
  this.subject = utils.sanitizeFieldPlainText(this.subject.trim())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
var utils = require('../helpers/utils')
  this.name = utils.sanitizeFieldPlainText(this.name.trim())
const utils = require('../helpers/utils')
  user.username = utils.sanitizeFieldPlainText(user.username.toLowerCase().trim())
  user.email = utils.sanitizeFieldPlainText(user.email.trim())

  if (user.fullname) user.fullname = utils.sanitizeFieldPlainText(user.fullname.trim())
  if (user.title) user.title = utils.sanitizeFieldPlainText(user.title.trim())