diff --git a/.planning/quick/260907-let-verbindungstest-fuer-das-postfach-im-aus/260907-let-PLAN.md b/.planning/quick/260907-let-verbindungstest-fuer-das-postfach-im-aus/260907-let-PLAN.md
new file mode 100644
index 0000000..7e68b4c
--- /dev/null
+++ b/.planning/quick/260907-let-verbindungstest-fuer-das-postfach-im-aus/260907-let-PLAN.md
@@ -0,0 +1,352 @@
+---
+phase: quick-260907-let
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - apps/api/src/tenders/tender-email-config.service.ts
+ - apps/api/src/tenders/tenders.controller.ts
+ - apps/api/src/tenders/tender-email-config.service.spec.ts
+ - apps/api/src/tenders/tenders.controller.spec.ts
+ - apps/web/src/lib/tender-radar-api.ts
+ - apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx
+ - apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.test.tsx
+ - apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx
+ - apps/web/src/messages/de.json
+ - apps/web/src/messages/en.json
+autonomous: true
+requirements: [WINDOWS-16]
+
+estimate:
+ tokens: 55000
+ raw_tokens: 55000
+ tasks: 2
+ confidence: low
+
+must_haves:
+ truths:
+ - "Ein angemeldeter Nutzer kann auf der Seite Meine Quellen die Verbindung zu seinem Postfach pruefen, ohne vorher zu speichern, und sieht entweder eine Erfolgsmeldung oder den Klartext-Fehler des Mailservers."
+ - "Ein bereits gespeichertes Postfach laesst sich erneut pruefen, ohne dass das Passwort neu eingetippt werden muss — der Server greift dann auf die verschluesselt hinterlegten Zugangsdaten des Nutzers zurueck."
+ - "Der Test laeuft ausschliesslich gegen das Postfach des angemeldeten Nutzers; eine im Rumpf mitgeschickte fremde Kennung kann daran nichts aendern."
+ - "Weder die Antwort des Servers noch seine Protokollzeilen enthalten Benutzername oder Passwort."
+ - "Die neuen Beschriftungen liegen in beiden Sprachdateien vor (Deutsch und Englisch)."
+ artifacts:
+ - "apps/api/src/tenders/tender-email-config.service.ts — Methode testConnection"
+ - "apps/api/src/tenders/tenders.controller.ts — Route POST email-config/test, deklariert vor @Get(':id')"
+ - "apps/web/src/lib/tender-radar-api.ts — Funktion testEmailConnection"
+ - "apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx — Knopf plus Rueckmeldung"
+ - "apps/web/src/messages/de.json und apps/web/src/messages/en.json — Schluessel unter tenderRadar.emailAlerts"
+ - "apps/api/src/tenders/tenders.controller.spec.ts — Deklarationsreihenfolge-Waechter um testEmailConnection erweitert"
+ key_links:
+ - "Controller-Handler -> extractTriageContext(req).userId -> Service-Aufruf (userId kommt nie aus dem Rumpf)"
+ - "Service -> ImapProvider.testConnection bzw. ExchangeInboxProvider.testConnection, ausgewaehlt nach dto.protocol"
+ - "Leeres Passwort im Rumpf -> entschluesselte encryptedInboxCreds derselben userId-Zeile"
+ - "EmailAlertConfigForm -> testEmailConnection -> POST /modules/tender-radar/email-config/test"
+ - "vi.mock-Fabriken fuer @/lib/tender-radar-api in EmailAlertConfigForm.test.tsx UND my-sources.test.tsx muessen testEmailConnection auffuehren — sonst wirft Vitest beim Import der Komponente"
+---
+
+
+Der Ausschreibungs-Radar bekommt fuer das Postfach unter "Meine Quellen" einen
+Verbindungstest — genau den, den das DKV-Modul seit Phase 7 hat. Schliesst
+WINDOWS-Eintrag #16.
+
+Purpose: Heute faellt ein Tippfehler in der EWS-Endpunkt-URL oder ein falsches
+Passwort erst dadurch auf, dass dauerhaft nichts ankommt — und dann ist nicht
+unterscheidbar, ob die Verbindung scheitert oder schlicht keine Alarm-Mail da
+war. Das blockiert unmittelbar den offenen Live-Test WINDOWS #12, dessen ganzer
+Zweck der Beleg des handgeschriebenen NTLM/SOAP-Wegs ist.
+
+Output: Ein per-Nutzer-Endpunkt POST /modules/tender-radar/email-config/test,
+ein Knopf "Verbindung testen" im Formular, Beschriftungen in beiden
+Sprachdateien, und ein erweiterter Deklarationsreihenfolge-Waechter.
+
+Die Faehigkeit selbst wird NICHT neu gebaut: beide Inbox-Provider bringen
+testConnection() bereits mit (apps/api/src/inbox/imap.provider.ts:343,
+apps/api/src/inbox/exchange-inbox.provider.ts:294, deklariert in
+apps/api/src/inbox/inbox-provider.interface.ts:31). Dieser Plan verdrahtet sie
+nur — das Vorbild ist DkvController.testConnection (dkv.controller.ts:106) und
+DkvService.testConnection (dkv.service.ts:201).
+
+
+
+@~/.claude/gsd-core/workflows/execute-plan.md
+@~/.claude/gsd-core/templates/summary.md
+
+
+
+@.planning/STATE.md
+@CLAUDE.md
+
+Vorbild Backend (gemessen, nicht aus Notizen):
+@apps/api/src/dkv/dkv.service.ts
+@apps/api/src/tenders/tender-email-config.service.ts
+@apps/api/src/tenders/tenders.controller.ts
+
+Vorbild Frontend:
+@apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx
+@apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx
+@apps/web/src/lib/tender-radar-api.ts
+
+
+
+
+
+ Task 1: Endpunkt POST email-config/test — vom Formularrumpf bis zum Mailserver
+ apps/api/src/tenders/tender-email-config.service.ts, apps/api/src/tenders/tenders.controller.ts, apps/api/src/tenders/tender-email-config.service.spec.ts, apps/api/src/tenders/tenders.controller.spec.ts
+
+ apps/api/src/dkv/dkv.service.ts (Methode testConnection, Zeilen 195-235) — die Vorlage inklusive Rueckfall auf die gespeicherten Zugangsdaten bei leerem Passwort.
+ apps/api/src/tenders/tender-email-config.service.ts (Methode saveConfig) — dort steht die bereits erprobte Entschluessel-und-Ergaenzen-Logik dieses Dienstes.
+ apps/api/src/tenders/tenders.controller.ts Zeilen 328-365 (die beiden email-config-Handler) und Zeile 582 (@Get(':id')).
+ apps/api/src/tenders/tenders.controller.spec.ts Zeilen 91-93 (makeFakeRequest), 132-140 (makeFakeEmailConfigService), 411-423 (der bestehende email-config-Reihenfolgetest).
+
+
+ - TenderEmailConfigService.testConnection(userId, dto) mit gefuelltem Passwort im Rumpf: reicht genau dieses Passwort an den Provider durch, liest die Datenbank gar nicht erst.
+ - Mit leerem Passwort im Rumpf und vorhandener gespeicherter Zeile: entschluesselt encryptedInboxCreds derselben userId und uebergibt das gespeicherte Passwort an den Provider.
+ - Mit leerem Benutzernamen im Rumpf: ergaenzt analog den gespeicherten Benutzernamen (gleiche Semantik wie saveConfig in derselben Datei).
+ - dto.protocol 'exchange' waehlt den Exchange-Provider, alles andere den IMAP-Provider.
+ - Die Rueckgabe ist unveraendert das, was der Provider liefert: { success: true } oder { success: false, message }. Kein Feld mehr, kein Feld weniger.
+ - TendersController.testEmailConnection nimmt userId aus extractTriageContext(req) und reicht es als erstes Argument weiter; ein im Rumpf mitgeschicktes Fremdfeld erreicht den Dienst nicht.
+ - Der bestehende Reihenfolgetest fuer email-config schlaegt fehl, sobald testEmailConnection nach getTender deklariert wird.
+
+
+ Schritt 1 — Dienst. In apps/api/src/tenders/tender-email-config.service.ts eine
+ Methode testConnection(userId: string, dto: TenderEmailConfigDto) ergaenzen, die
+ { success: boolean; message?: string } liefert. Sie baut ein InboxConfig-Objekt
+ (Typ aus '../inbox/inbox-provider.interface') aus den DTO-Feldern protocol, host,
+ port, username, password, encryption, folder, senderFilter, domain — mit denselben
+ Ersatzwerten wie DkvService.testConnection (host '' , port 993, folder 'INBOX').
+ Fehlen username oder password im DTO, wird die Zeile der uebergebenen userId gelesen
+ und encryptedInboxCreds entschluesselt; nur der jeweils fehlende Wert wird daraus
+ ergaenzt. Diese Rueckfall-Logik ist strukturell dieselbe, die saveConfig in derselben
+ Datei schon fuehrt — dort abschauen, nicht neu erfinden. Schlaegt das Entschluesseln
+ fehl, wird der Fehler still verschluckt und ohne Zugangsdaten weiterprobiert; eine
+ etwaige Protokollzeile nennt nur die userId, niemals einen Wert aus dem
+ Zugangsdatensatz (T-05-13, T-QT16-03). Anschliessend
+ protocol === 'exchange' ? exchangeProvider : imapProvider aufrufen und dessen
+ Ergebnis unveraendert zurueckgeben.
+
+ Die beiden Provider werden per Konstruktor injiziert. Sie MUESSEN als optionale
+ Parameter ANGEHAENGT werden (ImapProvider, ExchangeInboxProvider aus
+ '../inbox/imap.provider' bzw. '../inbox/exchange-inbox.provider') — genau nach dem
+ in diesem Repository dokumentierten Muster von TendersController.tenderIngestionService
+ (tenders.controller.ts Zeilen 95-103): so bleiben die acht bestehenden
+ new TenderEmailConfigService(prisma, crypto)-Aufrufe in der Spec ohne Aenderung
+ typkorrekt, waehrend Nest im Betrieb immer beide aufloest (InboxModule ist in
+ tenders.module.ts bereits importiert, ImapProvider und ExchangeInboxProvider werden
+ dort exportiert — nichts an der Modulverdrahtung ist anzufassen). Ist der benoetigte
+ Provider wider Erwarten nicht vorhanden, liefert testConnection
+ { success: false, message: 'Inbox-Anbieter nicht verfuegbar' } statt zu werfen, damit
+ die Antwortform der Route in jedem Fall stabil bleibt.
+
+ Schritt 2 — Route. In apps/api/src/tenders/tenders.controller.ts unmittelbar nach
+ saveEmailConfig (also klar VOR @Get(':id') in Zeile 582) einen Handler
+ testEmailConnection mit @Post('email-config/test') und @UseModule('tender-radar')
+ ergaenzen. Er liest const { userId } = this.extractTriageContext(req) und gibt
+ this.tenderEmailConfig.testConnection(userId, dto) zurueck. Der Konstruktor der
+ Klasse bleibt unangetastet — tenderEmailConfig ist bereits injiziert; das ist der
+ Grund, warum die Logik in den Dienst und nicht in den Controller gehoert (42
+ Konstruktoraufrufe in der Spec wuerden sonst brechen).
+
+ Den Handler mit einem Kommentarblock im Stil der Nachbarhandler versehen. Er soll
+ zwei Dinge festhalten: erstens, dass userId ausschliesslich aus dem
+ Authentifizierungskontext stammt und der DTO gar kein Kennungsfeld traegt (T-17-01 /
+ T-14-03-05, IDOR); zweitens, warum der Handler trotzdem oberhalb von @Get(':id')
+ steht, obwohl Nest Routen je HTTP-Verb aufloest und ein GET-Platzhalter eine
+ POST-Route heute nicht verdecken kann — der ganze email-config-Block bleibt beisammen,
+ und der Waechter greift auch dann noch, wenn spaeter einmal ein Platzhalter fuer POST
+ hinzukaeme. Diese Einordnung bitte woertlich so treffen, nicht als schlichtes
+ "sonst 404".
+
+ Schritt 3 — Tests. In apps/api/src/tenders/tender-email-config.service.spec.ts drei
+ Faelle ergaenzen, die den bestehenden Fabriken (makeFakePrisma, makeFakeCrypto)
+ folgen und zusaetzlich zwei winzige Provider-Attrappen mit einer testConnection-Zusage
+ uebergeben: (a) getipptes Passwort geht unveraendert an den Provider,
+ (b) leeres Passwort holt das gespeicherte, (c) protocol 'exchange' waehlt die
+ Exchange-Attrappe. In apps/api/src/tenders/tenders.controller.spec.ts erstens
+ makeFakeEmailConfigService um eine testConnection-Attrappe erweitern, zweitens einen
+ Fall ergaenzen, der mit makeFakeRequest('u1', ...) belegt, dass der Dienst mit 'u1'
+ aufgerufen wird, auch wenn der Rumpf ein abweichendes Kennungsfeld mitfuehrt, und
+ drittens den vorhandenen Reihenfolgetest in Zeile 411 um den Index von
+ testEmailConnection erweitern (kleiner als der Index von getTender).
+
+
+ cd apps/api && npx vitest run src/tenders/tender-email-config.service.spec.ts src/tenders/tenders.controller.spec.ts
+ cd apps/api && npx tsc --noEmit -p tsconfig.json
+ grep -n "email-config/test" apps/api/src/tenders/tenders.controller.ts
+
+
+ Beide Vitest-Dateien laufen gruen (Ausgangsstand: 8 + 52 Faelle, jetzt mehr), der
+ API-Typecheck bleibt fehlerfrei, und die Route POST email-config/test steht in
+ tenders.controller.ts an einer Zeilennummer unterhalb von saveEmailConfig und
+ oberhalb von @Get(':id').
+
+
+
+
+ Task 2: Knopf "Verbindung testen" im Postfach-Formular samt Beschriftungen
+ apps/web/src/lib/tender-radar-api.ts, apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.tsx, apps/web/src/messages/de.json, apps/web/src/messages/en.json, apps/web/src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.test.tsx, apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx
+
+ apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx Zeilen 140-205 (Zustandsfelder, update, handleTestConnection) und 520-580 (Knopfreihe und Rueckmeldung) — das nachzubildende Verhalten.
+ apps/web/src/lib/dkv-api.ts Zeilen 125-141 (testConnection) und apps/web/src/lib/tender-radar-api.ts Zeilen 518-571 (fetchEmailConfig/saveEmailConfig samt extractErrorMessage) — die Hausform des jeweiligen Moduls.
+ apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx Zeilen 14-22 — die vi.mock-Fabrik, die zwingend mitwachsen muss.
+
+
+ - Klick auf den Knopf ruft testEmailConnection genau einmal mit demselben Rumpf auf, den auch das Speichern erzeugt (formToPayload), also ohne Passwortfeld, solange keins getippt wurde.
+ - Waehrend der Anfrage tragen Test- und Speicherknopf den disabled-Zustand, und der Testknopf zeigt die Wartebeschriftung.
+ - Eine Antwort mit success false zeigt die Fehlermeldung des Servers sichtbar an.
+ - Eine Aenderung an irgendeinem Formularfeld raeumt die vorherige Rueckmeldung wieder weg.
+ - Die bestehenden fuenf Faelle in EmailAlertConfigForm.test.tsx bleiben unveraendert gruen.
+
+
+ Schritt 1 — Sprachdateien zuerst, damit die Komponente auf vorhandene Schluessel
+ zugreift. Unter tenderRadar.emailAlerts in apps/web/src/messages/de.json und
+ apps/web/src/messages/en.json jeweils vier Schluessel ergaenzen: testConnection,
+ testTesting, testSuccess und testFailed. Wortlaut und Platzhalter woertlich von
+ dkvFleet.form uebernehmen, wo dieselben vier Schluessel bereits stehen — deutsch
+ "Verbindung testen" / "Verbindung wird getestet..." / "Verbindung erfolgreich" /
+ "Verbindung fehlgeschlagen: {error}", englisch entsprechend. Deutsch ist die
+ Hauptsprache dieser Installation, aber beide Dateien muessen den Satz tragen.
+ Die 401-Hinweisliste des DKV-Formulars NICHT mitnehmen: sie gehoert dort zu einem
+ eigenen Hilfetext-Block, den dieses Formular nicht fuehrt.
+
+ Schritt 2 — Api-Klient. In apps/web/src/lib/tender-radar-api.ts eine exportierte
+ Funktion testEmailConnection ergaenzen, die denselben Nutzlast-Typ wie
+ saveEmailConfig entgegennimmt und Promise<{ success: boolean; message?: string }>
+ liefert. Sie sendet POST an ${API_URL}/modules/tender-radar/email-config/test mit
+ 'Content-Type': 'application/json' und credentials: 'include'. Bei nicht-ok-Antwort
+ wird extractErrorMessage genutzt — so machen es die beiden Nachbarfunktionen in
+ derselben Datei; die knappere Fehlerbehandlung aus dkv-api.ts hier bewusst nicht
+ uebernehmen.
+
+ Schritt 3 — Formular. In EmailAlertConfigForm.tsx testEmailConnection importieren,
+ die Zustandsfelder isTesting und testResult ergaenzen (Form wie in InboxConfigForm),
+ in update() zusaetzlich setTestResult(null) aufrufen und einen Handler
+ handleTestConnection schreiben, der formToPayload(form) sendet und Erfolg wie
+ Fehlschlag in testResult ablegt; ein geworfener Fehler landet als
+ { success: false, message } dort. In der Knopfreihe am Ende (heute nur "Speichern")
+ den Testknopf VOR dem Speicherknopf einsetzen, mit der Rahmen-Optik des DKV-Vorbilds,
+ disabled bei isTesting || isSaving; der Speicherknopf bekommt zusaetzlich isTesting
+ in seine disabled-Bedingung. Unter der Knopfreihe die Rueckmeldung rendern, gruen bei
+ Erfolg und in der Warnfarbe bei Fehlschlag, mit denselben Farbwerten, die das
+ Formular bereits fuer saveSuccess und saveError verwendet.
+
+ Der Beschreibungsblock ueber der Komponente (heute Zeilen 119-135) behauptet
+ ausdruecklich, dieses Formular fuehre keinen solchen Knopf und es gebe serverseitig
+ nichts, wogegen er pruefen koennte. Diese Passage ist ab jetzt falsch und muss
+ umgeschrieben werden: der Knopf ist da, er spricht die neue Route an, und die
+ Abrufhaeufigkeit bleibt weiterhin plattformweit geregelt (D-15) — nur Letzteres bleibt
+ als Begruendung stehen, warum es hier kein Intervallfeld gibt.
+
+ Schritt 4 — Attrappen nachziehen. Beide Testdateien, die
+ '@/lib/tender-radar-api' per vi.mock mit einer Fabrik ersetzen, muessen
+ testEmailConnection mitfuehren: EmailAlertConfigForm.test.tsx (Zeilen 8-11) und
+ my-sources.test.tsx (Zeilen 14-22). Fehlt der Eintrag, wirft Vitest bereits beim
+ Import der Komponente, weil der Named Export auf der Attrappe nicht existiert — das
+ ist der wahrscheinlichste Weg, diesen Task rot zu machen. In
+ EmailAlertConfigForm.test.tsx zusaetzlich die vier neuen Uebersetzungen in die
+ hand gepflegte Schluesseltabelle des next-intl-Mocks aufnehmen und zwei Faelle
+ ergaenzen: (a) Klick auf den Testknopf ruft testEmailConnection einmal mit einem
+ Rumpf ohne Passwortfeld auf, (b) eine Antwort { success: false, message: '401 ...' }
+ laesst die Fehlermeldung im Dokument erscheinen.
+
+
+ cd apps/web && npx vitest run "src/app/(portal)/modules/tender-radar/settings/components/EmailAlertConfigForm.test.tsx" "src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx"
+ cd apps/web && npx tsc --noEmit
+ node -e "const de=require('./apps/web/src/messages/de.json'),en=require('./apps/web/src/messages/en.json');const k=['testConnection','testTesting','testSuccess','testFailed'];for(const key of k){if(!de.tenderRadar.emailAlerts[key])throw new Error('de fehlt '+key);if(!en.tenderRadar.emailAlerts[key])throw new Error('en fehlt '+key);}console.log('locale keys ok');"
+
+ Abnahme im Browser — der Test beweist sich nur an einem echten Mailserver, nie
+ an Attrappen. Der Neubau und Neustart der Container liegt beim Nutzer; dieser
+ Plan fasst weder Docker noch den Testserver an.
+
+ 1. Portal oeffnen, Ausschreibungs-Radar, Seite "Meine Quellen", Abschnitt
+ "Mein Postfach". Unterhalb der Felder steht jetzt neben "Speichern" ein
+ zweiter Knopf "Verbindung testen".
+ 2. Absichtlich falsch pruefen: einen unsinnigen Servernamen eintragen (zum
+ Beispiel gibtesnicht.example) und den Testknopf druecken. Erwartung: kurz
+ "Verbindung wird getestet...", danach in Rot "Verbindung fehlgeschlagen:"
+ gefolgt vom Klartext des Mailservers.
+ 3. Richtig pruefen: echte Postfachdaten samt Passwort eintragen und erneut
+ druecken. Erwartung: gruene Zeile "Verbindung erfolgreich".
+ 4. Ohne Passwort nachpruefen: speichern, Seite neu laden (das Passwortfeld ist
+ danach absichtlich leer) und nur den Testknopf druecken. Erwartung: wieder
+ "Verbindung erfolgreich" — der Server hat auf die gespeicherten Zugangsdaten
+ zurueckgegriffen. Das ist der Punkt, der den Test im Alltag brauchbar macht.
+ 5. Kurzer Blick in die API-Protokollzeilen des Laufs: weder Benutzername noch
+ Passwort duerfen dort auftauchen.
+
+ Punkt 2 und 3 muessen sich unterscheiden — eine gruene Meldung bei absichtlich
+ falschem Server waere ein Fehlalarm und schlimmer als gar kein Knopf. Faellt
+ einer der Punkte durch, bleibt WINDOWS #16 offen; die gruenen Unit-Tests taugen
+ dann nicht als Gegenargument, sie pruefen ausschliesslich gegen Attrappen.
+
+
+
+ Beide Web-Testdateien laufen gruen (Ausgangsstand EmailAlertConfigForm: 5 Faelle,
+ jetzt 7), der Web-Typecheck bleibt fehlerfrei, und der Sprachdatei-Check meldet
+ "locale keys ok" fuer beide Sprachen. Die Browser-Abnahme oben wandert in die
+ Abnahmeliste am Phasenende (human_verify_mode = end-of-phase).
+
+
+
+
+
+
+## Trust Boundaries
+
+| Boundary | Description |
+|----------|-------------|
+| Browser -> API (POST /modules/tender-radar/email-config/test) | Nutzereingaben aus dem Formular, inklusive frei waehlbarem Servernamen und Zugangsdaten |
+| API -> Mailserver (IMAP/EWS) | Ausgehende Verbindung mit entschluesselten Zugangsdaten |
+| API -> Datenbank (encryptedInboxCreds) | Ruhende, verschluesselte Zugangsdaten eines einzelnen Nutzers |
+
+## STRIDE Threat Register
+
+| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
+|-----------|----------|-----------|----------|-------------|-----------------|
+| T-QT16-01 | Elevation of Privilege | TendersController.testEmailConnection | high | mitigate | userId stammt ausschliesslich aus extractTriageContext(req); TenderEmailConfigDto traegt gar kein Kennungsfeld, ein mitgeschicktes Fremdfeld erreicht den Dienst nicht (T-17-01 / T-14-03-05). Belegt durch den Controller-Testfall aus Task 1, Schritt 3 |
+| T-QT16-02 | Information Disclosure | Antwort und Protokoll des Tests | high | mitigate | Rueckgabe ist unveraendert das { success, message? } des Providers — kein Durchreichen des Konfigurationsobjekts. Der Dienst protokolliert im Fehlerfall hoechstens die userId; die Provider-Protokollzeilen sind seit T-07-03 bereits zugangsdatenfrei |
+| T-QT16-03 | Information Disclosure | entschluesselte gespeicherte Zugangsdaten | medium | mitigate | Entschluesselte Werte existieren nur im Methoden-Scope von testConnection, wandern ausschliesslich in das InboxConfig-Objekt und werden weder zurueckgegeben noch protokolliert (T-05-13) |
+| T-QT16-04 | Spoofing | frei waehlbarer host / EWS-Endpunkt im Testrumpf | medium | accept | Identische Oberflaeche und identisches Risiko wie das bereits bestehende PUT /email-config, das denselben Wert entgegennimmt und dauerhaft speichert. Der Test fuegt keine neue Angriffsflaeche hinzu; das Modul-Gate @UseModule('tender-radar') bleibt unveraendert davor |
+| T-QT16-05 | Denial of Service | wiederholtes Ausloesen des Tests | low | accept | Der Test bindet eine ausgehende Verbindung pro Klick, genau wie POST /dkv/test-connection seit Phase 7. Kein neues Muster, keine eigene Drosselung — dieselbe Bewertung wie beim Vorbild |
+
+Keine neuen Pakete: dieser Plan installiert nichts ueber npm, das
+Legitimitaets-Gate fuer Paketinstallationen ist daher nicht anwendbar.
+
+
+
+Nach Abschluss beider Tasks, aus dem Repository-Wurzelverzeichnis:
+
+```
+cd apps/api && npx vitest run && npx tsc --noEmit -p tsconfig.json
+cd apps/web && npx vitest run && npx tsc --noEmit
+```
+
+Gemessener Ausgangsstand vor Beginn dieses Plans: beide Typechecks fehlerfrei
+(Exit 0), apps/api/src/tenders/tender-email-config.service.spec.ts 8 Faelle
+gruen, apps/api/src/tenders/tenders.controller.spec.ts 52 Faelle gruen,
+EmailAlertConfigForm.test.tsx 5 Faelle gruen. Die Zahlen duerfen nur steigen.
+
+Der Reihenfolge-Waechter ist die dokumentierte Ausnahme von der
+Reparatur-auf-Zuruf-Regel dieses Repositories (Entscheidung vom 2026-08-11):
+er muss laut werden, wenn jemand die Handler spaeter umsortiert.
+
+
+
+- POST /modules/tender-radar/email-config/test existiert, ist mit
+ @UseModule('tender-radar') abgesichert und liefert { success, message? }.
+- Die Kennung des Postfachbesitzers stammt nachweislich aus dem
+ Authentifizierungskontext, nicht aus dem Rumpf — durch einen Testfall belegt.
+- Ein leer gelassenes Passwortfeld greift auf die gespeicherten,
+ verschluesselten Zugangsdaten desselben Nutzers zurueck.
+- Der Knopf "Verbindung testen" steht im Postfach-Formular unter "Meine
+ Quellen", zeigt einen Wartezustand und eine sichtbare Rueckmeldung.
+- Beide Sprachdateien tragen die vier neuen Schluessel.
+- Der Deklarationsreihenfolge-Waechter deckt testEmailConnection mit ab.
+- Alle vier Prueflaeufe (zwei Testsuiten, zwei Typechecks) bleiben gruen.
+- Die Browser-Abnahme aus dem human-check-Block von Task 2 ist bestaetigt (sie laeuft am Phasenende, nicht mitten im Lauf).
+
+
+