Audit: dev/sicherheit/SICHERHEITSAUDIT-2026-10-07.md, Plan und Entscheidungen:
dev/sicherheit/ENTWICKLUNGSPLAN-SICHERHEIT-2026-10-07.md.
- S-01 Legacy-Rechnungen fremder Kunden nur mit billing:manage (auch Kundenroute).
- S-02 Kontaktzuordnung auf der Firmenanzeige verlangt companies:write, $id gesperrt.
- S-03 CompanyMembershipObserver: automatische Firmenrechte werden bei jeder
Adress-/Zuordnungsänderung sofort neu bewertet; memberships:resync-auto.
- S-04 Passwortwechsel beendet alle anderen Sessions (PasswordChanger), Reset
alle Sessions und offene Login-Links; Fortify PUT /user/password abgeschaltet.
API-Tokens bleiben bestehen.
- S-05 Google: email_verified Pflicht, feste Bindung über die Provider-ID;
Erstverknüpfung bestehender Konten außerhalb gmail/hd nur per Bestätigungslink
in derselben Browser-Session.
- S-06 Frischer Identitätsnachweis (Passwort, App-Code, E-Mail-Code oder
Passkey) vor E-Mail-Wechsel, erster 2FA-Einrichtung und Token-Erzeugung;
Hinweis-Mails an die (bisherige) Kontoadresse.
- Entscheidung 5: Passkeys (Fortify, @laravel/passkeys) für alle; Super-Admins
und Admins bestätigen die Session für den Admin-Bereich per Passkey oder
App-Code (EnsureStrongAdminAuthentication, Notfallschalter
AUTH_REQUIRE_STRONG_ADMIN); Reset des zweiten Faktors durch Super-Admins.
Neue Migration: create_passkeys_table. Suite 2.403 grün (1 übersprungen),
Larastan ohne Meldung.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vier Punkte aus kontakterreichbarkeit-mx-bounces-2026-09-15.md, ohne
Veroeffentlichungssperre:
- PlausibleEmailDomain: Domain ohne Endung oder mit Tippfehler-TLD ist
ein 422 an der Legacy-API, deterministisch ohne DNS.
- email_domain_checks + MailDomainChecker ueber DnsResolver-Schnittstelle
(RFC 5321 impliziter MX, RFC 7505 Null MX, Unresolved ist kein Befund),
Observer an Company/Contact, Job nach Commit, taeglicher Refresh,
mail:check-domains fuer den Bestand. Nie eine Adresssperre aus DNS.
- Admin > Reports > Einlieferer-Bericht: tote Adressen aus Sperrliste
und Domainbefund, automatisch abgelehnte Meldungen mit Grund,
Text zum Kopieren und CSV. Versendet nichts.
- EmailBounceKind::Policy: 5.7.x wird protokolliert, zaehlt aber nicht
auf den Schwellwert. Statuscode entscheidet vor Diagnosetext.
Messung vorab: 6 von 877 API-Domains ohne Zustellweg, eine davon fuer
66 der 151 Ruecklaeufer verantwortlich.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Admin ▸ Mail-Sperrliste mit Freigabe, manueller Sperre, nicht lesbaren
Rückläufern und Breaker-Zustand. Firmenseite markiert gesperrte
Firmen-E-Mail und Pressekontakte. Die Claim-Seite zählt gesperrte
Adressen nicht mehr als Empfänger und erklärt, warum kein Link geht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Unterseite /admin/newsletter/abmeldungen listet Newsletter-Opt-outs mit
Zeitpunkt, Suche und Link zur User-Detailseite
- Kampagnen-Filter: Empfänger einer früheren Kampagne ausschließen
(Abgleich per User-ID und E-Mail-Snapshot) für Follow-up-Mailings
- Login-Filter kann Monats-Scheiben (von–bis) abgrenzen, um große
Listen Monat für Monat abzuarbeiten
- Versand läuft in Wellen (Standard 50 Mails / 15 Min, per .env
justierbar) gegen SMTP-Limits und Blocklisten; Hinweis im Senden-Modal
- web: Firmen-Website im Newsroom & Co. als absolute URL verlinkt
(Company::websiteUrl), statt relativ auf /newsroom/www.example.de
- kunde: API-Doku-Button öffnet die /de/api-Seite; OpenAPI-Spezifikation
als eigener, benannter Download (pressekonto-api-v1.yml)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In companies/index, companies/show und contacts/index war die private
Methode portalBadgeColor() definiert, aber nirgends aufgerufen (kein
Aufruf im Blade-Markup oder PHP). Entfernt; in companies/show wird damit
auch der Portal-Import ungenutzt und faellt weg.
Klaert den im Wiedereinstieg-Plan offenen Punkt: keine Portal::badgeColor()-
Zentralisierung noetig, da es keine echten Call-Sites gab.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Anzeigename eines Kontakts (Vor-/Nachname mit Fallback) als Model-Methode.
Ersetzt die identische Inline-Logik an 6 eindeutigen Relation-$contact-Stellen
(Admin: Benutzer-/PM-/Firmen-Detail; Kunde: Press-Kit-/PM-Detail).
Bewusst noch offen: Select-/toBase-Varianten ($contactOption/$contactResult/
$lookupContact, stdClass-Risiko) sowie web/release-detail (User-Frontend).
Test: ContactDisplayNameTest.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>