fix: Input-Validation auf allen API-Routen vereinheitlichen (Phase 5, Schritt 27)
Alle Routen nutzen jetzt das zentrale Validierungsmodul (validate.js): - Maximale Stringlängen (200 Titel, 5000 Text, 100 Kurztexte) - Enum-Validation für Kategorien, Prioritäten, Meal-Types - Datum/Zeit/DateTime-Format-Prüfung - RRULE-Validation (neue rrule()-Funktion) - Farbwert-Prüfung (#RRGGBB) Betroffene Routen: calendar, notes, contacts, budget, shopping, meals. Tasks-Route um RRULE-Validation ergänzt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,15 @@
|
||||
const MAX_TITLE = 200;
|
||||
const MAX_TEXT = 5000;
|
||||
const MAX_SHORT = 100;
|
||||
const MAX_RRULE = 300;
|
||||
|
||||
// Regex-Muster
|
||||
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
||||
const TIME_RE = /^\d{2}:\d{2}$/;
|
||||
const DATETIME_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?Z?)?$/;
|
||||
const COLOR_RE = /^#[0-9A-Fa-f]{6}$/;
|
||||
const MONTH_RE = /^\d{4}-\d{2}$/;
|
||||
const RRULE_RE = /^(FREQ=(DAILY|WEEKLY|MONTHLY)(;INTERVAL=\d{1,2})?(;BYDAY=[A-Z,]{2,}(,[A-Z]{2})*)?(;UNTIL=\d{8}(T\d{6}Z)?)?)?$/;
|
||||
|
||||
/**
|
||||
* Bereinigt und validiert einen Pflicht-String.
|
||||
@@ -103,4 +112,54 @@ function collectErrors(results) {
|
||||
return results.map((r) => r.error).filter(Boolean);
|
||||
}
|
||||
|
||||
module.exports = { str, oneOf, date, time, num, color, collectErrors, MAX_TITLE, MAX_TEXT, MAX_SHORT };
|
||||
/**
|
||||
* Validiert ein Datetime-Format YYYY-MM-DD oder YYYY-MM-DDTHH:MM[:SS][Z].
|
||||
*/
|
||||
function datetime(val, field, required = false) {
|
||||
if (!val) {
|
||||
if (required) return { value: null, error: `${field} ist erforderlich.` };
|
||||
return { value: null, error: null };
|
||||
}
|
||||
if (!DATETIME_RE.test(String(val)))
|
||||
return { value: null, error: `${field} muss im Format YYYY-MM-DD oder YYYY-MM-DDTHH:MM sein.` };
|
||||
return { value: String(val), error: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validiert ein Monatsformat YYYY-MM.
|
||||
*/
|
||||
function month(val, field) {
|
||||
if (!val) return { value: null, error: null };
|
||||
if (!MONTH_RE.test(String(val)))
|
||||
return { value: null, error: `${field} muss im Format YYYY-MM sein.` };
|
||||
return { value: String(val), error: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validiert eine optionale RRULE.
|
||||
*/
|
||||
function rrule(val, field) {
|
||||
if (!val) return { value: null, error: null };
|
||||
const s = String(val).trim();
|
||||
if (s.length > MAX_RRULE)
|
||||
return { value: null, error: `${field} darf maximal ${MAX_RRULE} Zeichen haben.` };
|
||||
// Grundlegende Struktur: KEY=VALUE;KEY=VALUE
|
||||
if (!/^FREQ=(DAILY|WEEKLY|MONTHLY)/.test(s))
|
||||
return { value: null, error: `${field}: Ungültige Wiederholungsregel.` };
|
||||
return { value: s, error: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validiert eine ganzzahlige ID (positiv).
|
||||
*/
|
||||
function id(val, field) {
|
||||
const n = parseInt(val, 10);
|
||||
if (!n || n < 1) return { value: null, error: `${field} muss eine positive Zahl sein.` };
|
||||
return { value: n, error: null };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
str, oneOf, date, time, datetime, month, num, color, rrule, id, collectErrors,
|
||||
MAX_TITLE, MAX_TEXT, MAX_SHORT, MAX_RRULE,
|
||||
DATE_RE, TIME_RE, DATETIME_RE, COLOR_RE, MONTH_RE,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user