diff --git a/backend/src/services/stressfreiEmail.service.ts b/backend/src/services/stressfreiEmail.service.ts index bcde2ffe..10e5a7bf 100644 --- a/backend/src/services/stressfreiEmail.service.ts +++ b/backend/src/services/stressfreiEmail.service.ts @@ -566,6 +566,12 @@ export async function syncForwardingForEmail( // additionalForwardingEmails-Liste rein. Der nachfolgende Plesk-Call // deaktiviert dann die Mailgroup und schreibt die volle Liste als // Forwarding. Verlustfrei – kein Empfänger fällt raus. + // Pentest 83.2: Self-Forward auch beim Import blocken. Die + // Stressfrei-Adresse selbst darf nicht aus Plesk in unsere DB + // landen – sonst läuft sie nach dem Mailgroup→Forwarding-Umschalten + // als Forwarding-Target auf sich selbst (Mail-Loop). + seenKeys.add(canonicalEmailKey(stressfreiEmail.email)); + try { const pleskState = await checkEmailExists(localPart); const existingMembers = [ @@ -574,11 +580,26 @@ export async function syncForwardingForEmail( ]; const newImports: string[] = []; for (const member of existingMembers) { - const key = canonicalEmailKey(member); + // Pentest 83.1: importierte Adressen aus Plesk müssen denselben + // Filter passieren wie User-Eingaben (TLD-Blocklist, Format). + // Sonst rutschen reservierte TLDs wie `.internal` ohne Check + // in unsere DB, falls ein Plesk-Admin sie dort manuell gepflegt + // hat. Ungültige werden silent gedroppt – Log informiert. + let validated: string; + try { + validated = assertValidForwardingEmail(member); + } catch (validationErr) { + const reason = validationErr instanceof Error ? validationErr.message : 'unbekannt'; + console.debug( + `[syncForwardingForEmail] Plesk-Member "${member}" verworfen: ${reason}`, + ); + continue; + } + const key = canonicalEmailKey(validated); if (!seenKeys.has(key)) { seenKeys.add(key); - forwardTargets.push(member); - newImports.push(member); + forwardTargets.push(validated); + newImports.push(validated); } } if (newImports.length > 0) { @@ -590,7 +611,8 @@ export async function syncForwardingForEmail( where: { id }, data: { additionalForwardingEmails: serializeAdditionalForwards(mergedAdditional) }, }); - console.log( + // Pentest 83.3: PII-Logs auf debug-Level statt log-Level. + console.debug( `[syncForwardingForEmail] Importiert aus Plesk-Mailgroup für ${stressfreiEmail.email}:`, newImports, ); diff --git a/docs/todo.md b/docs/todo.md index 05d74003..d6fa2bb8 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -97,6 +97,26 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung ## ✅ Erledigt +- [x] **🔒 Pentest 83.1-83.3: Auto-Import-Pfad härten** + - **83.1 MEDIUM:** Auto-Import in `syncForwardingForEmail` umging + `assertValidForwardingEmail`. Plesk-Member wie `attacker@plesk.internal` + oder `evil@x.local` wären ohne TLD-Block-Check (71.1) in unsere + DB gewandert. Fix: jeder importierte Member läuft durch + `assertValidForwardingEmail`; ungültige werden silent gedroppt + und auf `console.debug`-Level geloggt. + - **83.2 LOW:** Self-Forward-Schutz (81.1) lief nur im Add-Pfad. + Wenn Plesk die eigene Adresse als Mailgroup-Member führte, wäre + sie beim Auto-Import in die DB-Liste gerutscht → nach dem + Umschalten auf Forwarding Mail-Loop. Fix: + `seenKeys.add(canonicalEmailKey(stressfreiEmail.email))` vor + der Import-Schleife. + - **83.3 INFO:** PII-Log auf `console.debug` umgestellt (statt + `console.log` auf Default-Level). + - Smoke-Test mit gemischter Plesk-Liste: `karibik61@web.de` (legit) + importiert, `attacker@plesk.internal` + `evil@x.local` per 83.1 + abgelehnt, exakte Self-Mail + Plus-Tag-Variante per 83.2 + abgelehnt, Customer-Stamm-Mail + Default deduped. + - [x] **🐞 Plesk-Sync: Legacy-Mailgroup-Adressen synchronisierten nicht** - Prod-Bug: User trägt zusätzliche Weiterleitung ein, Toast meldet Erfolg, aber Plesk übernimmt nichts. Ursache: Plesk hat zwei