🎉 Fix: Address security vulnerability in admin-unified module & related files (#XXXXXX)

This commit is contained in:
2025-06-16 11:17:10 +02:00
parent 4b2ff50f7a
commit 061bae3005
6 changed files with 663 additions and 98 deletions

View File

@ -332,12 +332,8 @@ class AdminDashboard {
try {
const url = `${this.apiBaseUrl}/api/stats`;
const response = await fetch(url);
const data = await this.validateApiResponse(response, 'Live-Statistiken');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
this.updateStatsDisplay(data);
this.retryCount = 0;
@ -347,6 +343,8 @@ class AdminDashboard {
if (this.retryCount <= this.maxRetries) {
setTimeout(() => this.loadLiveStats(), 5000);
} else {
this.showNotification('Live-Statistiken konnten nicht geladen werden', 'error');
}
}
}
@ -624,12 +622,7 @@ class AdminDashboard {
async loadUserData(userId) {
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
const data = await this.validateApiResponse(response, 'Benutzerdaten laden');
if (data.success) {
const user = data.user;
@ -649,7 +642,7 @@ class AdminDashboard {
}
} catch (error) {
console.error('Fehler beim Laden der Benutzerdaten:', error);
this.showNotification('❌ Fehler beim Laden der Benutzerdaten', 'error');
this.showNotification(`❌ Fehler beim Laden der Benutzerdaten: ${error.message}`, 'error');
}
}
@ -680,7 +673,7 @@ class AdminDashboard {
body: JSON.stringify(userData)
});
const data = await response.json();
const data = await this.validateApiResponse(response, 'Benutzer erstellen');
if (data.success) {
this.showNotification('✅ Benutzer erfolgreich erstellt!', 'success');
@ -895,20 +888,101 @@ class AdminDashboard {
}
// Error-Management
/**
* Zentrale API-Response-Validierung mit umfassendem Error-Handling
* @param {Response} response - Fetch Response-Objekt
* @param {string} context - Kontext der API-Anfrage für bessere Fehlermeldungen
* @returns {Promise<Object>} - Validierte JSON-Daten
* @throws {Error} - Bei Validierungsfehlern
*/
async validateApiResponse(response, context = 'API-Anfrage') {
try {
// 1. HTTP Status Code prüfen
if (!response.ok) {
// Spezielle Behandlung für bekannte Fehler-Codes
switch (response.status) {
case 401:
throw new Error(`Authentifizierung fehlgeschlagen (${context})`);
case 403:
throw new Error(`Zugriff verweigert (${context})`);
case 404:
throw new Error(`Ressource nicht gefunden (${context})`);
case 429:
throw new Error(`Zu viele Anfragen (${context})`);
case 500:
throw new Error(`Serverfehler (${context})`);
case 503:
throw new Error(`Service nicht verfügbar (${context})`);
default:
throw new Error(`HTTP ${response.status}: ${response.statusText} (${context})`);
}
}
// 2. Content-Type prüfen (muss application/json enthalten)
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
// Versuche Response-Text zu lesen für bessere Fehlermeldung
const responseText = await response.text();
// Prüfe auf HTML-Fehlerseiten (typisch für 404/500 Seiten)
if (responseText.includes('<!DOCTYPE html>') || responseText.includes('<html')) {
console.warn(`❌ HTML-Fehlerseite erhalten statt JSON (${context}):`, responseText.substring(0, 200));
throw new Error(`Server-Fehlerseite erhalten statt JSON-Response (${context})`);
}
console.warn(`❌ Ungültiger Content-Type (${context}):`, contentType);
console.warn(`❌ Response-Text (${context}):`, responseText.substring(0, 500));
throw new Error(`Ungültiger Content-Type: ${contentType || 'fehlt'} (${context})`);
}
// 3. JSON parsing mit detailliertem Error-Handling
let data;
try {
data = await response.json();
} catch (jsonError) {
// Versuche rohen Text zu lesen für Debugging
const rawText = await response.text();
console.error(`❌ JSON-Parsing-Fehler (${context}):`, jsonError);
console.error(`❌ Raw Response (${context}):`, rawText.substring(0, 1000));
throw new Error(`Ungültige JSON-Response: ${jsonError.message} (${context})`);
}
// 4. Prüfe auf null/undefined Response
if (data === null || data === undefined) {
throw new Error(`Leere Response erhalten (${context})`);
}
// 5. Validiere Response-Struktur (wenn success-Feld erwartet wird)
if (typeof data === 'object' && data.hasOwnProperty('success')) {
if (!data.success && data.error) {
console.warn(`❌ API-Fehler (${context}):`, data.error);
throw new Error(`API-Fehler: ${data.error} (${context})`);
}
}
// Erfolgreiche Validierung
console.log(`✅ API-Response validiert (${context}):`, data);
return data;
} catch (error) {
// Error-Logging mit Kontext
console.error(`❌ validateApiResponse fehlgeschlagen (${context}):`, error);
console.error(`❌ Response-Details (${context}):`, {
status: response.status,
statusText: response.statusText,
url: response.url,
headers: Object.fromEntries(response.headers.entries())
});
// Re-throw mit erweiterten Informationen
throw error;
}
}
async checkSystemHealth() {
try {
const response = await fetch(`${this.apiBaseUrl}/api/admin/system/status`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Prüfe Content-Type vor JSON parsing
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Server returned non-JSON response');
}
const data = await response.json();
const data = await this.validateApiResponse(response, 'System-Health-Check');
if (data.success) {
this.updateHealthDisplay(data);
@ -916,6 +990,7 @@ class AdminDashboard {
}
} catch (error) {
console.error('Fehler bei System-Health-Check:', error);
this.showNotification(`System-Health-Check fehlgeschlagen: ${error.message}`, 'error');
}
}