fix(security): address critical and high findings from security audit

Fix stored XSS in tasks (titles/subtasks) and settings (member list)
by applying escHtml(). Harden trust proxy to loopback default, add
OAuth state parameter for Google Calendar CSRF protection, sanitize
CSV export against formula injection, invalidate sessions on user
deletion, restrict usernames to alphanumeric chars, and require admin
role for calendar sync triggers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas
2026-04-03 17:28:36 +02:00
parent 1122bd269b
commit 3d2604bab9
10 changed files with 96 additions and 20 deletions
+4
View File
@@ -53,6 +53,10 @@ function getCredentials() {
}
function saveCredentials(url, username, password) {
// Warnung wenn DB-Verschluesselung nicht aktiv - Credentials liegen dann im Klartext
if (!process.env.DB_ENCRYPTION_KEY) {
console.warn('[Apple] WARNUNG: DB_ENCRYPTION_KEY nicht gesetzt - CalDAV-Credentials werden unverschluesselt gespeichert.');
}
cfgSet('apple_caldav_url', url);
cfgSet('apple_username', username);
cfgSet('apple_app_password', password);
+11 -1
View File
@@ -14,6 +14,7 @@
'use strict';
const { google } = require('googleapis');
const crypto = require('crypto');
const db = require('../db');
const GOOGLE_COLOR = '#4285F4';
@@ -92,12 +93,21 @@ function loadAuthorizedClient() {
* Generiert die Google OAuth2-URL zum Weiterleiten des Admins.
* @returns {string} Auth-URL
*/
function getAuthUrl() {
/**
* Generiert die Google OAuth2-URL zum Weiterleiten des Admins.
* Enthalt einen CSRF-sicheren state-Parameter.
* @param {object} session - Express-Session-Objekt (state wird dort gespeichert)
* @returns {string} Auth-URL
*/
function getAuthUrl(session) {
const client = createClient();
const state = crypto.randomBytes(32).toString('hex');
if (session) session.googleOAuthState = state;
return client.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: ['https://www.googleapis.com/auth/calendar'],
state,
});
}