Security & UX Release v7
Security: - H1: Stored-XSS-Fix — Upload-Pfad-Whitelist (Server + Frontend-Guard safeFileUrl) - H2: Transaktionen repariert — txDb-Contract in db.js (PG + SQLite), Rollback funktioniert - H3-Vorbereitung: SESSION_SECRET wird in Compose durchgereicht (Fix M3) - registerLimiter exportiert (Crash-Bug: Route.post ohne Callback) - LDAP-Sync: PG-Transaktionsabbruch bei UNIQUE-Verstoß behoben (Precheck-Selects) - LDAP-Filter: nur echte Benutzerkonten (keine Computer/Service-Accounts, Bit 512) - Rollen app-seitig: Sync ändert nie role/status, neue User immer user+inaktiv - DB-Cleanup: 82 Computer-/Service-Accounts aus lokaler User-Tabelle entfernt UX: - Dashboard: Vorlagen als Table-Liste + Column-Chart (Top 5 in %), 2 gleich große Spalten - Table-Listen (Dashboard/Vorlageneditor/Aufgaben) scrollbar bis Seitenende - Pagination 10/Seite im Dashboard, Sidebar-Label Dashboard
This commit is contained in:
@@ -77,17 +77,35 @@ const updateTemplateSchema = z.object({
|
||||
});
|
||||
|
||||
// ============ Task Schemas ============
|
||||
// H1: Whitelist für Datei-Pfade — nur Pfade akzeptieren, die exakt vom
|
||||
// Upload-Endpoint emittiert werden (/api/upload/uploads/<ts>-<rand>-<name>.<ext>).
|
||||
// Verhindert Stored XSS über javascript:/data:-URLs in Task-Dateilinks.
|
||||
// Endungen beschränkt auf die vom Uploader erlaubten Typen (inkl. .bin-Fallback
|
||||
// für abgelehnte Original-Endungen). Der Name-Teil ist bewusst `*` (nicht `+`),
|
||||
// da der Uploader auch Dateien mit leerem Basisnamen erzeugen kann (z.B. ".pdf").
|
||||
const UPLOAD_PATH_PATTERN = /^\/api\/upload\/uploads\/[0-9]+-[0-9]+-[a-zA-Z0-9._-]*\.(pdf|png|jpg|jpeg|gif|txt|doc|docx|bin)$/;
|
||||
const filePathSchema = z.string().regex(UPLOAD_PATH_PATTERN, 'Ungueltiger Dateipfad.');
|
||||
|
||||
/**
|
||||
* H1: Prüft, ob ein Pfad/URL ein legitimer Upload-Link ist.
|
||||
* Wird auch in routes/tasks.js verwendet, um `value`-Felder von
|
||||
* file_upload-Steps zu validieren (dort ist der Step-Typ erst serverseitig bekannt).
|
||||
*/
|
||||
function isSafeUploadPath(path) {
|
||||
return typeof path === 'string' && UPLOAD_PATH_PATTERN.test(path);
|
||||
}
|
||||
|
||||
const createTaskSchema = z.object({
|
||||
template_id: z.number().int().positive('Template-ID ist erforderlich.'),
|
||||
title: z.string().min(1, 'Titel ist erforderlich.').max(500),
|
||||
user_id: z.number().int().positive().optional(),
|
||||
file_path: z.string().optional(),
|
||||
file_path: filePathSchema.optional(),
|
||||
// V9: Limit task values array to prevent DoS via huge payloads
|
||||
values: z.array(z.object({
|
||||
step_id: z.number().int().positive().optional(),
|
||||
value: z.string().max(10000).optional(),
|
||||
is_checked: z.boolean().optional(),
|
||||
file_path: z.string().optional(),
|
||||
file_path: filePathSchema.optional(),
|
||||
})).max(100, 'Maximal 100 Werte pro Aufgabe erlaubt.').optional().default([]),
|
||||
});
|
||||
|
||||
@@ -206,4 +224,5 @@ module.exports = {
|
||||
// Middleware
|
||||
validate,
|
||||
validateQuery,
|
||||
isSafeUploadPath,
|
||||
};
|
||||
Reference in New Issue
Block a user