CVE-2022-2595: Improper Authorization (CWE-285)
value: Improper Authorization in GitHub repository kromitgmbh/titra prior to 0.79.1.
CWE description: The product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.
CVSS v3 base score: 10
Published: 2022-08-01T15:15Z
Repository: https://github.com/kromitgmbh/titra
Language: JavaScript
Files changed: imports/api/customfields/server/methods.js, imports/api/extensions/methods.js, imports/api/globalsettings/methods.js, imports/ui/components/projectTasks.js, imports/ui/pages/administration.js, package.json
Commit date: 2022-06-27 19:14:15 +0200
Commit message: 🐛 fixed a bug in the oidc configuration preventing saving changes
🔒 fixed admin authorization validation for all administration pages
🔒 fixed a stored XSS vulnerability in the task functionality

Diff: diff --git a/imports/api/customfields/server/methods.js b/imports/api/customfields/server/methods.js
index 6b97159..629e730 100644
--- a/imports/api/customfields/server/methods.js
+++ b/imports/api/customfields/server/methods.js
@@ -1,18 +1,18 @@
 import CustomFields from '../customfields.js'
-import { checkAuthentication } from '../../../utils/server_method_helpers'
+import { checkAdminAuthentication } from '../../../utils/server_method_helpers'
 
 Meteor.methods({
   addCustomField: function addCustomField({
     classname, name, desc, type, possibleValues,
   }) {
     check(classname, String)
     check(name, String)
     check(type, String)
     check(desc, String)
     check(possibleValues, Match.Maybe([String]))
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     if (CustomFields.findOne({ name })) {
       throw new Meteor.Error('error-custom-field-exists', 'Custom field already exists', { method: 'addCustomField' })
     }
     const customField = {
       classname,
@@ -26,20 +26,20 @@ Meteor.methods({
     CustomFields.insert(customField)
     return customField
   },
   removeCustomField: function removeCustomField({ _id }) {
     check(_id, String)
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     if (!CustomFields.findOne({ _id })) {
       throw new Meteor.Error('error-custom-field-not-found', 'Custom field not found', { method: 'removeCustomField' })
     }
     CustomFields.remove({ _id })
   },
   updateCustomField: function updateCustomField({
     _id, desc, type, possibleValues,
   }) {
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     check(_id, String)
     check(type, String)
     check(desc, String)
     check(possibleValues, Match.Maybe([String]))
     if (!CustomFields.findOne({ _id })) {
diff --git a/imports/api/extensions/methods.js b/imports/api/extensions/methods.js
index 1780fa8..72934ca 100644
--- a/imports/api/extensions/methods.js
+++ b/imports/api/extensions/methods.js
@@ -1,12 +1,12 @@
 import AdmZip from 'adm-zip'
 import Extensions from './extensions'
-import { checkAuthentication } from '../../utils/server_method_helpers'
+import { checkAdminAuthentication } from '../../utils/server_method_helpers'
 
 Meteor.methods({
   addExtension({ zipFile }) {
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     const regex = /^data:.+\/(.+);base64,(.*)$/
     const matches = zipFile.match(regex)
     const data = matches[2]
     let zip
     if (data) {
@@ -34,29 +34,29 @@ Meteor.methods({
       return 'notifications.success'
     }
     return new Meteor.Error('Extension has been added before.')
   },
   removeExtension({ extensionId }) {
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     const extension = Extensions.findOne({ _id: extensionId })
     if (extension) {
       Extensions.remove({ _id: extension._id })
       return 'notifications.success'
     }
     return new Meteor.Error('Extension does not exist.')
   },
   launchExtension({ extensionId }) {
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     const extension = Extensions.findOne({ _id: extensionId })
     if (extension) {
       eval(extension.server)
       return 'notifications.success'
     }
     return new Meteor.Error('Extension does not exist')
   },
   toggleExtensionState({ extensionId, state }) {
-    checkAuthentication(this)
+    checkAdminAuthentication(this)
     const extension = Extensions.findOne({ _id: extensionId })
     if (extension) {
       Extensions.update({ _id: extension._id }, { $set: { isActive: state } })
       return 'notifications.success'
     }
diff --git a/imports/api/globalsettings/methods.js b/imports/api/globalsettings/methods.js
index f5ace0f..2932c6a 100644
--- a/imports/api/globalsettings/methods.js
+++ b/imports/api/globalsettings/methods.js
@@ -28,12 +28,13 @@ Meteor.methods({
         break
       }
     }
   },
   updateOidcSettings(configuration) {
+    check(configuration, Object)
     checkAdminAuthentication(this)
     ServiceConfiguration.configurations.remove({
       service: 'oidc',
     })
     ServiceConfiguration.configurations.insert(configuration)
-  }
+  },
 })
diff --git a/imports/ui/components/projectTasks.js b/imports/ui/components/projectTasks.js
index eb90e9d..37cfb7b 100644
--- a/imports/ui/components/projectTasks.js
+++ b/imports/ui/components/projectTasks.js
@@ -85,11 +85,11 @@ Template.projectTasks.onRendered(() => {
         })
       }
       const ganttTasks = tasks.fetch()?.map((task) => (
         {
           id: task._id,
-          name: task.name,
+          name: $('span').text(task.name).get(0).innerHTML,
           start: dayjs(task.start).format('YYYY-MM-DD'),
           end: dayjs(task.end).format('YYYY-MM-DD'),
           dependencies: task.dependencies,
         }))
       const ganttOptions = {
diff --git a/imports/ui/pages/administration.js b/imports/ui/pages/administration.js
index 1dd3b16..2f6cc87 100644
--- a/imports/ui/pages/administration.js
+++ b/imports/ui/pages/administration.js
@@ -287,11 +287,11 @@ Template.administration.events({
       service: 'oidc',
       loginStyle: 'popup',
     }
 
     // Fetch the value of each input field
-    Oidc.fields.forEach((field) => {
+    oidcFields.forEach((field) => {
       configuration[field.property] = document.getElementById(
         `configure-oidc-${field.property}`
       ).value.replace(/^\s*|\s*$/g, '') // trim() doesnt work on IE8
     })
 
diff --git a/package.json b/package.json
index fd5120f..cc509e9 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "titra",
-  "version": "0.79.0",
+  "version": "0.79.1",
   "private": true,
   "scripts": {
     "start": "meteor run"
   },
   "dependencies": {

Vulnerable code: import { checkAuthentication } from '../../../utils/server_method_helpers'
    checkAuthentication(this)
    checkAuthentication(this)
    checkAuthentication(this)
import { checkAuthentication } from '../../utils/server_method_helpers'
    checkAuthentication(this)
    checkAuthentication(this)
    checkAuthentication(this)
    checkAuthentication(this)
  }
          name: task.name,
    Oidc.fields.forEach((field) => {
  "version": "0.79.0",
Fixed code: import { checkAdminAuthentication } from '../../../utils/server_method_helpers'
    checkAdminAuthentication(this)
    checkAdminAuthentication(this)
    checkAdminAuthentication(this)
import { checkAdminAuthentication } from '../../utils/server_method_helpers'
    checkAdminAuthentication(this)
    checkAdminAuthentication(this)
    checkAdminAuthentication(this)
    checkAdminAuthentication(this)
    check(configuration, Object)
  },
          name: $('span').text(task.name).get(0).innerHTML,
    oidcFields.forEach((field) => {
  "version": "0.79.1",