CVE-2022-1752 (published 2022-05-21T03:15Z)

Unrestricted Upload of File with Dangerous Type in GitHub repository polonel/trudesk prior to 1.2.2.

Severity: MEDIUM
CVSS v2 base score: 6.0 (AV:N/AC:M/Au:S/C:P/I:P/A:P)
CVSS v3 base score: 8.0, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-434
cwe_name: Unrestricted Upload of File with Dangerous Type
description: The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
extended_description: 
            
            
            
            
            
         
         

Fix information:
repo_url: https://github.com/polonel/trudesk
commit_details: repo_url: https://github.com/polonel/trudesk
author: Chris Brame
author_date: 2022-05-16 23:54:40-04:00
msg: refactor(accounts): security enhancements
repository: repo_url: https://github.com/polonel/trudesk
repo_name: polonel/trudesk
description:  :coffee: :seedling: Trudesk is an open-source help desk/ticketing solution.
repo_language: JavaScript
owner: polonel
file_changes: filename: accounts.js
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,7 @@ const userSchema = require('../models/user')
 const permissions = require('../permissions')
 const emitter = require('../emitter')
 const xss = require('xss')
+const path = require('path')
 
 const accountsController = {}
 
@@ -462,6 +463,8 @@ accountsController.uploadImage = function (req, res) {
     }
   })
 
+  const allowedExts = ['.png', '.jpg', '.jpeg', '.gif']
+
   const object = {}
   let error
 
@@ -474,7 +477,8 @@ accountsController.uploadImage = function (req, res) {
     const filename = info.filename
     const mimetype = info.mimeType
     const ext = path.extname(filename)
-    if (mimetype.indexOf('image/') === -1 || ext === '.svg') {
+
+    if (!allowedExts.includes(ext)) {
       error = {
         status: 400,
         message: 'Invalid File Type'
@@ -486,8 +490,9 @@ accountsController.uploadImage = function (req, res) {
     const savePath = path.join(__dirname, '../../public/uploads/users')
     if (!fs.existsSync(savePath)) fs.mkdirSync(savePath)
 
-    object.filePath = path.join(savePath, 'aProfile_' + object.username + path.extname(filename))
     object.filename = 'aProfile_' + object.username + path.extname(filename)
+    object.filename = object.filename.replace('/', '').replace('..', '')
+    object.filePath = path.join(savePath, object.filename)
     object.mimetype = mimetype
 
     file.on('limit', function () {

programming_language: JavaScript
filename: routes.js
change_type: ModificationType.MODIFY
diff: @@ -130,7 +130,6 @@ module.exports = function (middleware, router, controllers) {
   router.get('/api/v1/users/:username', apiv1, canUser('accounts:view'), apiCtrl.users.single)
   router.put('/api/v1/users/:username', apiv1, canUser('accounts:update'), apiCtrl.users.update)
   router.get('/api/v1/users/:username/groups', apiv1, apiCtrl.users.getGroups)
-  router.post('/api/v1/users/:username/uploadprofilepic', apiv1, apiCtrl.users.uploadProfilePic)
   router.put('/api/v1/users/:username/updatepreferences', apiv1, apiCtrl.users.updatePreferences)
   router.get('/api/v1/users/:username/enable', apiv1, canUser('accounts:update'), apiCtrl.users.enableUser)
   router.delete('/api/v1/users/:username', apiv1, canUser('accounts:delete'), apiCtrl.users.deleteUser)

programming_language: CoffeeScript
filename: users.js
change_type: ModificationType.MODIFY
diff: @@ -1080,85 +1080,6 @@ apiUsers.getGroups = function (req, res) {
   }
 }
 
-apiUsers.uploadProfilePic = function (req, res) {
-  const fs = require('fs')
-  const path = require('path')
-  const Busboy = require('busboy')
-  const busboy = Busboy({
-    headers: req.headers,
-    limits: {
-      files: 1,
-      fileSize: 1024 * 1024 * 3
-    }
-  })
-
-  const object = {}
-  let error
-
-  if (_.isUndefined(req.params.username)) return res.status(400).json({ error: 'Invalid Username' })
-  object.username = req.params.username
-
-  busboy.on('file', function (name, file, info) {
-    const mimetype = info.mimeType
-
-    if (mimetype.indexOf('image/') === -1) {
-      error = {
-        status: 400,
-        message: 'Invalid file type'
-      }
-
-      return file.resume()
-    }
-
-    const savePath = path.join(__dirname, '../../../../public/uploads/users')
-    if (!fs.existsSync(savePath)) fs.mkdirSync(savePath)
-
-    object.filePath = path.join(savePath, 'aProfile_' + object.username + '.jpg')
-    object.filename = 'aProfile_' + object.username + '.jpg'
-    object.mimetype = mimetype
-
-    file.on('limit', function () {
-      error = {
-        status: 400,
-        message: 'File too large'
-      }
-
-      return file.resume()
-    })
-
-    file.pipe(fs.createWriteStream(object.filePath))
-  })
-
-  busboy.on('finish', function () {
-    if (error) return res.status(error.status).send(error.message)
-
-    if (_.isUndefined(object.username) || _.isUndefined(object.filePath) || _.isUndefined(object.filename)) {
-      return res.status(400).send('Invalid Form Data')
-    }
-
-    if (!fs.existsSync(object.filePath)) return res.status(400).send('File failed to save to disk')
-
-    UserSchema.getUserByUsername(object.username, function (err, user) {
-      if (err) return res.status(400).send(err.message)
-
-      user.image = object.filename
-
-      user.save(function (err) {
-        if (err) return res.status(500).send(err.message)
-
-        emitter.emit('trudesk:profileImageUpdate', {
-          userid: user._id,
-          img: user.image
-        })
-
-        return res.json({ success: true, user: stripUserFields(user) })
-      })
-    })
-  })
-
-  req.pipe(busboy)
-}
-
 function stripUserFields (user) {
   user.password = undefined
   user.accessToken = undefined

programming_language: JavaScript