4ab0340473
R95.1 MEDIUM: foo\r\nBcc:evil@x.de → Header-Injection-Vektor R95.3 LOW: <script>...</script>@x.de → silent stripHtml-Mutation R95.4 LOW: >190 Zeichen → VARCHAR-Overflow → 500 statt 400 Fix: validatePortalUsername() in sanitize.ts mit Whitelist ^[A-Za-z0-9_\-/.@+ ]{0,100}$. Strukturell sind CRLF, Tab, alle Control-Chars, Tags und Quotes raus → R95.1+R95.3 ohne extra Check. Max 100 → ApiError(400) → R95.4. Raw-Input vor stripHtml geprüft (R87-Pattern). Eingehängt in sanitizeContractBody. R95.2 (Email-Format-Pflicht) bewusst NICHT übernommen: portalUsername ist im Manual-Modus nicht zwingend eine Email (Vodafone, 1&1, EWE und Stadtwerke nutzen Kundennummern oder Pseudonyme als Portal-Login). Doku in SECURITY-HARDENING.md § Runde 95. Frontend: maxLength={100} am Input als UX-Schicht. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>