🎉 Fix: Address security vulnerability in admin-unified module & related files (#XXXXXX)
This commit is contained in:
@ -26,6 +26,97 @@ function detectApiBaseUrl() {
|
||||
|
||||
const API_BASE_URL = detectApiBaseUrl();
|
||||
|
||||
/**
|
||||
* 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 function 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;
|
||||
}
|
||||
}
|
||||
|
||||
// DOM-Elemente
|
||||
const elements = {
|
||||
activeJobs: null,
|
||||
@ -83,17 +174,13 @@ function initializeDashboard() {
|
||||
// Dashboard-Hauptdaten laden
|
||||
async function loadDashboardData() {
|
||||
try {
|
||||
const response = await fetch('/api/dashboard');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
dashboardData = await response.json();
|
||||
const response = await fetch(`${API_BASE_URL}/api/dashboard`);
|
||||
dashboardData = await validateApiResponse(response, 'Dashboard-Daten');
|
||||
updateDashboardUI();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Dashboard-Daten:', error);
|
||||
showError('Fehler beim Laden der Dashboard-Daten');
|
||||
showError(`Fehler beim Laden der Dashboard-Daten: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,18 +208,14 @@ function updateDashboardUI() {
|
||||
// Aktuelle Jobs laden
|
||||
async function loadRecentJobs() {
|
||||
try {
|
||||
const response = await fetch('/api/jobs/recent');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const response = await fetch(`${API_BASE_URL}/api/jobs/recent`);
|
||||
const data = await validateApiResponse(response, 'Aktuelle Jobs');
|
||||
updateRecentJobsList(data.jobs);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der aktuellen Jobs:', error);
|
||||
if (elements.recentJobsList) {
|
||||
elements.recentJobsList.innerHTML = '<li class="list-group-item text-danger">Fehler beim Laden</li>';
|
||||
elements.recentJobsList.innerHTML = `<li class="list-group-item text-danger">Fehler beim Laden: ${error.message}</li>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,18 +250,14 @@ function updateRecentJobsList(jobs) {
|
||||
// Aktuelle Aktivitäten laden
|
||||
async function loadRecentActivities() {
|
||||
try {
|
||||
const response = await fetch('/api/activity/recent');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const response = await fetch(`${API_BASE_URL}/api/activity/recent`);
|
||||
const data = await validateApiResponse(response, 'Aktivitäten');
|
||||
updateRecentActivitiesList(data.activities);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Aktivitäten:', error);
|
||||
if (elements.recentActivitiesList) {
|
||||
elements.recentActivitiesList.innerHTML = '<li class="list-group-item text-danger">Fehler beim Laden</li>';
|
||||
elements.recentActivitiesList.innerHTML = `<li class="list-group-item text-danger">Fehler beim Laden: ${error.message}</li>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,12 +288,8 @@ function updateRecentActivitiesList(activities) {
|
||||
// Scheduler-Status laden
|
||||
async function loadSchedulerStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/scheduler/status');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const response = await fetch(`${API_BASE_URL}/api/scheduler/status`);
|
||||
const data = await validateApiResponse(response, 'Scheduler-Status');
|
||||
updateSchedulerStatus(data.running);
|
||||
|
||||
} catch (error) {
|
||||
@ -253,11 +328,7 @@ async function toggleScheduler() {
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
const result = await validateApiResponse(response, 'Scheduler umschalten');
|
||||
|
||||
if (result.success) {
|
||||
showSuccess(result.message);
|
||||
|
Reference in New Issue
Block a user