docs(quick-260921-iwr): 30 Stellen geprueft, kein echter Fehler darunter
Zusammenfassung und STATE.md zum Quick-Vorgang 260921-iwr. Damit ist der fehlerverdaechtige Lint-Rueckstand vollstaendig geprueft. Beide Verdachtsmomente widerlegt, durch Messung statt Argument: die LDAP-Seite nutzt fuer die Liste, die wirklich waechst und schrumpft, laengst eine stabile Kennung; und im Cert-Manager enden alle vier Pfade mit einer 83-Byte-Schrottdatei bei 400, nie 500 - node-forge wirft, statt still ein falsches Zertifikat zu bauen. Ein Fund dreht die Richtung um: bei grants/page.tsx waere die Korrektur schaedlich, dort ist die Positionsnummer fuer die Eindeutigkeit noetig. Geaendert: 5 Stellen. 25 bleiben bewusst stehen und bleiben in der Zaehlung sichtbar, mit Begruendung je Stelle in der Akte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
+5
-4
@@ -4,8 +4,8 @@ milestone: v1.2
|
|||||||
current_phase: 18
|
current_phase: 18
|
||||||
current_phase_name: desktop-client-fertigstellen
|
current_phase_name: desktop-client-fertigstellen
|
||||||
status: verified
|
status: verified
|
||||||
stopped_at: "Sechs Quick-Vorgaenge am 2026-09-21 abgeschlossen (9ie, a1d, bi2, fi3, gof, i8x). Offen als naechste Fehlerklasse: Listenschluessel per Positionsnummer (19 im Quellcode) und Ausrufezeichen-Zusicherungen (11 im Quellcode). Danach kommen zwei neue Widgets. i8x ist noch nicht gepusht."
|
stopped_at: "Sieben Quick-Vorgaenge am 2026-09-21 abgeschlossen und verifiziert (9ie, a1d, bi2, fi3, gof, i8x, iwr). Der fehlerverdaechtige Lint-Rueckstand ist damit vollstaendig geprueft: zwei echte Fehler gefunden und behoben (tote Passwortwechsel-Sperre an der API, stille 403-Antworten in der Benutzerverwaltung), der Rest war harmlos oder Absicht, je Stelle in den Akten begruendet. Offen und NICHT fehlerverdaechtig: 288 any im Quellcode, 30 a11y-Befunde mit Gestaltungsbedarf. Naechster Auftrag des Nutzers: zwei neue Dashboard-Widgets."
|
||||||
last_updated: "2026-09-21T12:00:00.000Z"
|
last_updated: "2026-09-21T12:45:00.000Z"
|
||||||
last_activity: 2026-09-21
|
last_activity: 2026-09-21
|
||||||
last_activity_desc: Quick 260921-9ie, a1d, bi2, fi3 und gof — Lint-Tor scharf, Benutzerverwaltung meldet abgewiesene Aktionen, Lint-Rueckstand 2923 → 446, erzwungener Passwortwechsel an der API durchgesetzt (war eine tote Sperre), 21 Effekt-Abhaengigkeiten einzeln beurteilt; alle fuenf verifiziert, die letzten drei am laufenden System
|
last_activity_desc: Quick 260921-9ie, a1d, bi2, fi3 und gof — Lint-Tor scharf, Benutzerverwaltung meldet abgewiesene Aktionen, Lint-Rueckstand 2923 → 446, erzwungener Passwortwechsel an der API durchgesetzt (war eine tote Sperre), 21 Effekt-Abhaengigkeiten einzeln beurteilt; alle fuenf verifiziert, die letzten drei am laufenden System
|
||||||
state_head: 4d485432c003a6caf68f6d85aff7de0bd27794e2
|
state_head: 4d485432c003a6caf68f6d85aff7de0bd27794e2
|
||||||
@@ -31,7 +31,7 @@ See: .planning/PROJECT.md (updated 2026-07-17)
|
|||||||
Phase: 18 (desktop-client-fertigstellen) — COMPLETE (2026-09-17, Verifikation passed, Windows-Bedienprobe bestanden)
|
Phase: 18 (desktop-client-fertigstellen) — COMPLETE (2026-09-17, Verifikation passed, Windows-Bedienprobe bestanden)
|
||||||
Plan: 6 of 6
|
Plan: 6 of 6
|
||||||
Status: Alle 18 Phasen abgeschlossen; Version 1.2.0 freigegeben. Kein laufender Meilenstein. Nach 1.2.0 auf main (Beta): Bildmarke in Akzentfarbe, CI-Desktop-Skip, Favoriten-Symbol/-Sortierung, Desktop-Server-Adresse, Update in der App (signiert), Versionszeile auf der Setup-Seite — alles verifiziert und auf VM/CI nachgewiesen
|
Status: Alle 18 Phasen abgeschlossen; Version 1.2.0 freigegeben. Kein laufender Meilenstein. Nach 1.2.0 auf main (Beta): Bildmarke in Akzentfarbe, CI-Desktop-Skip, Favoriten-Symbol/-Sortierung, Desktop-Server-Adresse, Update in der App (signiert), Versionszeile auf der Setup-Seite — alles verifiziert und auf VM/CI nachgewiesen
|
||||||
Last activity: 2026-09-21 - Quick 260921-i8x: fuenf fehlerverdaechtige Lint-Klassen geprueft, kein echter Fehler darunter; Weiterleitungsschutz safe-next.ts von rohen Steuerbytes auf Escapes gehaertet, Gleichwertigkeit ueber alle 65536 Codepunkte nachgerechnet (54 abgelehnte Zeichen, null Abweichung)
|
Last activity: 2026-09-21 - Quick 260921-iwr: 30 Listenschluessel- und Zusicherungs-Stellen geprueft, kein echter Fehler; beide Verdachtsmomente (LDAP-Seite, Cert-Manager) durch Messung widerlegt. Der fehlerverdaechtige Rueckstand ist damit abgearbeitet
|
||||||
|
|
||||||
Progress: [██████████] 99%
|
Progress: [██████████] 99%
|
||||||
|
|
||||||
@@ -450,6 +450,7 @@ Gerettet aus `.continue-here.md`. Relevant fuer die noch offenen Live-Tests.
|
|||||||
| 260921-fi3 | **Erzwungener Passwortwechsel wurde an der API nie durchgesetzt — Sicherheitsfix.** Aus dem Lint-Durchlauf 260921-bi2 kamen fuenf gemeldete "Symptome". Alle am laufenden System nachgestellt: eines widerlegt (Passwortwechsel-Seite leitet sehr wohl weiter, siehe bi2-VERIFICATION), drei bestaetigt, eines (ZIP-Dateiname) bewusst nicht angefasst. **Der schwere Befund:** `auth.service.ts:176` legt `mustChangePassword` in den JWT, `jwt.strategy.ts` liess das Feld beim Auspacken fallen, also war `request.user.mustChangePassword` immer `undefined` und der global registrierte `ForcePasswordChangeInterceptor` eine Attrappe — er hat seit seiner Einfuehrung nie etwas blockiert. Durchgesetzt wurde der Zwangswechsel allein von der Web-Middleware; jeder Weg daran vorbei (Desktop-App, Skript, curl) umging ihn. Der Kommentar des Interceptors behauptete woertlich "T-02-14: Prevents bypass via direct API access" — das war falsch. Kein Rechteausbau: die eigene Rolle bleibt, aber der Zwang entfaellt. **Gemessen vorher:** Sitzung mit `mustChangePassword=true` bekam auf `GET /users` **200 samt vollstaendiger Benutzerliste**. **Behoben:** Strategie reicht das Feld durch (strikt `=== true`, fehlender Anspruch in alten Sitzungen wird `false`), Erlaubnisliste von Teilzeichenketten-Vergleich auf exaktes Verfahren+Pfad umgestellt. **Sauberer Nachweis** (gleicher Nutzer, gleiche Rolle, gleiche Route, nur die Kennzeichnung unterscheidet sich — auf `/users` haette der Rollen-Riegel das Ergebnis verdeckt): `GET /modules/active` → 403 `{"message":"FORCE_PASSWORD_CHANGE"}` mit Zwang, 200 ohne. `/auth/me`, `/auth/change-password` und `/auth/logout` kommen weiterhin durch. **Rot-dann-Gruen belegt:** neue Spezifikationen gegen den alten Stand 6 von 12 rot, danach 12/12 gruen — vom Verifier unabhaengig nachgestellt (alte Dateien aus `116041b` rekonstruiert). Gezielt nach Schlupfloechern gesucht (Schraegstrich am Ende, Abfragezeichen, Gross/Klein, `../`): keins. **Kein Aussperren:** kompletter Browser-Ablauf durchgespielt — Anmeldung leitet auf `/change-password`, Seite bedienbar, Wechsel gelingt, landet auf `/`, Kennzeichnung geloescht, freie Navigation. Die Seitenleiste zeigt waehrenddessen "Keine Module" (neuer 403 auf `/modules/active`, wortlos geschluckt) — sachlich richtig. **Dazu Fahrzeugtabelle (dkv-fleet):** Loeschknopf war doppelt ausloesbar (`isDeleting` wurde geschrieben, nie gelesen; Dialog blieb waehrend der Anfrage offen) — beide Dialogknoepfe jetzt gesperrt. Nebenbefund des Verifiers: die Wirkung kommt vom `disabled`-Attribut, React unterdrueckt Klicks darauf selbst; der Zustandscheck ist redundant, nicht falsch. Ausserdem sieben fest verdrahtete deutsche Texte und sechs Vorlese-Beschriftungen auf next-intl umgestellt (de und en, 87 Schluessel deckungsgleich). **Nicht angefasst, begruendet:** `SplitTab.tsx` `'certificates.zip'` — ein Downloadname ist ein Dateisystem-Artefakt, kein Bedienelement; uebersetzt braechte er Umlaute in Windows-Dateifreigaben. **Balken:** api 71 Dateien/1136 Tests, web 66/462, type-check 4/4, `pnpm lint` 5/5 ohne Fehlerstufe, Warnungen 466. Verifikation passed (9/9). | 2026-09-21 | f7c02b7,e56cce4 | [260921-fi3-erzwungener-passwortwechsel-wird-von-der](./quick/260921-fi3-erzwungener-passwortwechsel-wird-von-der/) |
|
| 260921-fi3 | **Erzwungener Passwortwechsel wurde an der API nie durchgesetzt — Sicherheitsfix.** Aus dem Lint-Durchlauf 260921-bi2 kamen fuenf gemeldete "Symptome". Alle am laufenden System nachgestellt: eines widerlegt (Passwortwechsel-Seite leitet sehr wohl weiter, siehe bi2-VERIFICATION), drei bestaetigt, eines (ZIP-Dateiname) bewusst nicht angefasst. **Der schwere Befund:** `auth.service.ts:176` legt `mustChangePassword` in den JWT, `jwt.strategy.ts` liess das Feld beim Auspacken fallen, also war `request.user.mustChangePassword` immer `undefined` und der global registrierte `ForcePasswordChangeInterceptor` eine Attrappe — er hat seit seiner Einfuehrung nie etwas blockiert. Durchgesetzt wurde der Zwangswechsel allein von der Web-Middleware; jeder Weg daran vorbei (Desktop-App, Skript, curl) umging ihn. Der Kommentar des Interceptors behauptete woertlich "T-02-14: Prevents bypass via direct API access" — das war falsch. Kein Rechteausbau: die eigene Rolle bleibt, aber der Zwang entfaellt. **Gemessen vorher:** Sitzung mit `mustChangePassword=true` bekam auf `GET /users` **200 samt vollstaendiger Benutzerliste**. **Behoben:** Strategie reicht das Feld durch (strikt `=== true`, fehlender Anspruch in alten Sitzungen wird `false`), Erlaubnisliste von Teilzeichenketten-Vergleich auf exaktes Verfahren+Pfad umgestellt. **Sauberer Nachweis** (gleicher Nutzer, gleiche Rolle, gleiche Route, nur die Kennzeichnung unterscheidet sich — auf `/users` haette der Rollen-Riegel das Ergebnis verdeckt): `GET /modules/active` → 403 `{"message":"FORCE_PASSWORD_CHANGE"}` mit Zwang, 200 ohne. `/auth/me`, `/auth/change-password` und `/auth/logout` kommen weiterhin durch. **Rot-dann-Gruen belegt:** neue Spezifikationen gegen den alten Stand 6 von 12 rot, danach 12/12 gruen — vom Verifier unabhaengig nachgestellt (alte Dateien aus `116041b` rekonstruiert). Gezielt nach Schlupfloechern gesucht (Schraegstrich am Ende, Abfragezeichen, Gross/Klein, `../`): keins. **Kein Aussperren:** kompletter Browser-Ablauf durchgespielt — Anmeldung leitet auf `/change-password`, Seite bedienbar, Wechsel gelingt, landet auf `/`, Kennzeichnung geloescht, freie Navigation. Die Seitenleiste zeigt waehrenddessen "Keine Module" (neuer 403 auf `/modules/active`, wortlos geschluckt) — sachlich richtig. **Dazu Fahrzeugtabelle (dkv-fleet):** Loeschknopf war doppelt ausloesbar (`isDeleting` wurde geschrieben, nie gelesen; Dialog blieb waehrend der Anfrage offen) — beide Dialogknoepfe jetzt gesperrt. Nebenbefund des Verifiers: die Wirkung kommt vom `disabled`-Attribut, React unterdrueckt Klicks darauf selbst; der Zustandscheck ist redundant, nicht falsch. Ausserdem sieben fest verdrahtete deutsche Texte und sechs Vorlese-Beschriftungen auf next-intl umgestellt (de und en, 87 Schluessel deckungsgleich). **Nicht angefasst, begruendet:** `SplitTab.tsx` `'certificates.zip'` — ein Downloadname ist ein Dateisystem-Artefakt, kein Bedienelement; uebersetzt braechte er Umlaute in Windows-Dateifreigaben. **Balken:** api 71 Dateien/1136 Tests, web 66/462, type-check 4/4, `pnpm lint` 5/5 ohne Fehlerstufe, Warnungen 466. Verifikation passed (9/9). | 2026-09-21 | f7c02b7,e56cce4 | [260921-fi3-erzwungener-passwortwechsel-wird-von-der](./quick/260921-fi3-erzwungener-passwortwechsel-wird-von-der/) |
|
||||||
| 260921-gof | **21 React-Effekt-Abhaengigkeiten einzeln beurteilt — 15 davon waren Fallen, nicht Fehler.** Die Klasse war aus 260921-bi2 zurueckgestellt worden, weil jeder Befund einzeln zu beurteilen ist. Ergebnis: nur **2 echte Defekte** (A), **15 Fallen** (B, das naive Eintragen haette eine Abruf-Schleife erzeugt), **3 Absicht** (C, mit begruendetem `biome-ignore` — erste Verwendung im Projekt), **1 Ballast** (D). **Die gefaehrlichste Stelle:** `calendar-widget.tsx:82` — `showToday` setzt bei jedem Klick ein frisches `Date`; `monthDate` naiv in die Liste einzutragen haette **jeden** Druck auf den Monatstitel einen Termin-Abruf ausloesen lassen, ueber die API bis zum Exchange-Server. Reihenfolge war Pflicht: erst Identitaet stabilisieren, dann die Liste umstellen. **Die haeufigste Falle:** `t` aus `useTranslations` ist in diesem Projekt bei jedem Durchlauf eine frische Funktion (die Test-Attrappen sind nachweislich so gebaut) — 8 Befunde. Griff ohne Ausnahme-Kommentar: den uebersetzten Text vor dem Hook in eine Konstante ziehen und diese eintragen; React vergleicht Zeichenketten per Wert. **Nebenbefund:** 11 `eslint-disable`-Zeilen fuer genau diese Regel waren wirkungslos, seit Biome ESLint abgeloest hat — alle entfernt. **Laufzeitnachweis vom Orchestrator im Browser** (Netzwerkprotokoll, nie `fetch` aus der Seite; gegen neu gebaute Abbilder): Dashboard 62 s Ruhe → Protokoll byte-identisch, genau 1 `calendar/events`; Monatstitel 3x gedrueckt → nur der erste Druck (Bereich aendert sich wirklich) loest einen Abruf aus, Druck 2 und 3 **null**; "Weiter" 3x → 3 Abrufe, korrekt; Stoppuhr 6 s real → Anzeige 00:06, 4 Runden ueber 4,8 s → 16/17/19/20 monoton, kein Ruecksprung. Dazu acht weitere Ansichten je 20-25 s ruhen gelassen (Marktplatz, Modulverwaltung, Gruppenverwaltung, DKV dreimal, Ausschreibungsradar zweimal) — jeder Endpunkt genau einmal. `InvoiceHistoryTable` hatte als einzige Datei keinen Test und ist damit gemessen statt nur gelesen; `ResultsList` ist die Stelle, an der die `t`-Falle in bi2 tatsaechlich zuschnappte. **Zahlen:** Warnungen 467 → 446 (exakt 21, nichts anderswo gewachsen), `useExhaustiveDependencies` 0, web-Tests 66/462 → 67/477, api 71/1136 unveraendert, type-check 4/4, `pnpm lint` 5/5 ohne Fehlerrang. Verifikation passed. **Benannt, nicht behoben:** zwei Verschwendungen im Kalender-Abruffenster (gleicher Zeitbereich zweimal geholt; `calendar/sources` bei jedem Monatswechsel) — vorbestehend; und `t` in vier vorbestehenden Abhaengigkeitslisten ausserhalb des Auftrags, die Biome nie gemeldet hat. | 2026-09-21 | b3f0e3c,e2c508c,e780b2c | [260921-gof-effekt-abhaengigkeiten-in-react-21-befun](./quick/260921-gof-effekt-abhaengigkeiten-in-react-21-befun/) |
|
| 260921-gof | **21 React-Effekt-Abhaengigkeiten einzeln beurteilt — 15 davon waren Fallen, nicht Fehler.** Die Klasse war aus 260921-bi2 zurueckgestellt worden, weil jeder Befund einzeln zu beurteilen ist. Ergebnis: nur **2 echte Defekte** (A), **15 Fallen** (B, das naive Eintragen haette eine Abruf-Schleife erzeugt), **3 Absicht** (C, mit begruendetem `biome-ignore` — erste Verwendung im Projekt), **1 Ballast** (D). **Die gefaehrlichste Stelle:** `calendar-widget.tsx:82` — `showToday` setzt bei jedem Klick ein frisches `Date`; `monthDate` naiv in die Liste einzutragen haette **jeden** Druck auf den Monatstitel einen Termin-Abruf ausloesen lassen, ueber die API bis zum Exchange-Server. Reihenfolge war Pflicht: erst Identitaet stabilisieren, dann die Liste umstellen. **Die haeufigste Falle:** `t` aus `useTranslations` ist in diesem Projekt bei jedem Durchlauf eine frische Funktion (die Test-Attrappen sind nachweislich so gebaut) — 8 Befunde. Griff ohne Ausnahme-Kommentar: den uebersetzten Text vor dem Hook in eine Konstante ziehen und diese eintragen; React vergleicht Zeichenketten per Wert. **Nebenbefund:** 11 `eslint-disable`-Zeilen fuer genau diese Regel waren wirkungslos, seit Biome ESLint abgeloest hat — alle entfernt. **Laufzeitnachweis vom Orchestrator im Browser** (Netzwerkprotokoll, nie `fetch` aus der Seite; gegen neu gebaute Abbilder): Dashboard 62 s Ruhe → Protokoll byte-identisch, genau 1 `calendar/events`; Monatstitel 3x gedrueckt → nur der erste Druck (Bereich aendert sich wirklich) loest einen Abruf aus, Druck 2 und 3 **null**; "Weiter" 3x → 3 Abrufe, korrekt; Stoppuhr 6 s real → Anzeige 00:06, 4 Runden ueber 4,8 s → 16/17/19/20 monoton, kein Ruecksprung. Dazu acht weitere Ansichten je 20-25 s ruhen gelassen (Marktplatz, Modulverwaltung, Gruppenverwaltung, DKV dreimal, Ausschreibungsradar zweimal) — jeder Endpunkt genau einmal. `InvoiceHistoryTable` hatte als einzige Datei keinen Test und ist damit gemessen statt nur gelesen; `ResultsList` ist die Stelle, an der die `t`-Falle in bi2 tatsaechlich zuschnappte. **Zahlen:** Warnungen 467 → 446 (exakt 21, nichts anderswo gewachsen), `useExhaustiveDependencies` 0, web-Tests 66/462 → 67/477, api 71/1136 unveraendert, type-check 4/4, `pnpm lint` 5/5 ohne Fehlerrang. Verifikation passed. **Benannt, nicht behoben:** zwei Verschwendungen im Kalender-Abruffenster (gleicher Zeitbereich zweimal geholt; `calendar/sources` bei jedem Monatswechsel) — vorbestehend; und `t` in vier vorbestehenden Abhaengigkeitslisten ausserhalb des Auftrags, die Biome nie gemeldet hat. | 2026-09-21 | b3f0e3c,e2c508c,e780b2c | [260921-gof-effekt-abhaengigkeiten-in-react-21-befun](./quick/260921-gof-effekt-abhaengigkeiten-in-react-21-befun/) |
|
||||||
| 260921-i8x | **Fuenf fehlerverdaechtige Lint-Klassen geprueft — kein einziger echter Fehler darunter.** Zwoelf Stellen einzeln beurteilt, Ergebnis: 7x gleichwertig oder Absicht, 2x Haertung, 3x idiomatisch korrekt. Das ist das Ergebnis, keine Ausrede — die Klassen klangen gefaehrlicher als sie waren. **Die eine Stelle mit echtem Wert:** `apps/web/src/lib/safe-next.ts`, der Schutz gegen Weiterleitung auf fremde Seiten nach der Anmeldung. Der Kommentar dort behauptete, die Steuerzeichen stuenden als Unicode-Escapes im Muster; die rohen Bytes zeigten das Gegenteil (NUL, 0x1F, 0x7F direkt eingebettet). Funktionierte, war aber zerbrechlich: verschluckt ein Werkzeug das NUL-Byte, wird aus dem Bereich stillschweigend ein anderer und der Schutz loechrig — der Rueckgabewert landet in `login/page.tsx` direkt in `window.location.href`. Jetzt echte Escapes, Datei ohne ein einziges Steuerbyte. **Beweis der Gleichwertigkeit, nicht Behauptung:** ueber alle 65536 Codepunkte dieselbe Menge abgelehnter Zeichen — 54 Stueck (32 Steuerzeichen 0x00-0x1F, dazu 0x7F, Backslash und die 20 Leerraum-Zeichen von `\s`), null Abweichung; vom Orchestrator unabhaengig gegen ein selbst gebautes Referenzmuster nachgerechnet. **Bewusst nicht angefasst:** die NUL-Maskierung in `ldap.service.ts` (RFC 4515) — genau dieses Zeichen zu treffen ist ihr Zweck, wer sie "repariert", oeffnet LDAP-Filter-Injection. Ebenso die drei `while ((m = re.exec(s)))`-Schleifen (idiomatisch, kein verrutschtes Gleichheitszeichen) und `noUselessSwitchCase` aus bi2. **Zwei Korrekturen an frueheren Annahmen:** `sanitizeNextPath` laeuft NICHT in der Edge-Middleware (die importiert nur `buildNextParam`), und das blosse Umschreiben auf Escapes senkt die Warnzahl nicht — Biome beanstandet die Escape-Schreibweise genauso, es braucht zusaetzlich einen einzeiligen Unterdrueckungskommentar. **Werkzeugfalle, dreimal zugeschnappt:** das Schreibwerkzeug wandelt `\uXXXX` still in das echte Zeichen um — der Planer erzeugte so zehn rohe Steuerbytes in seiner ersten Planfassung, der Executor zweimal in Commit-Text und Akte (git verweigerte den Commit wegen eines NUL-Bytes), und der Orchestrator beim Nachrechnen. Umgehung ueber `python3`/`chr(92)` ist im Plan hinterlegt. **Zahlen:** 446 → 434, `noControlCharactersInRegex`/`useIterableCallbackReturn`/`noGlobalIsNan` je 0, `suppressions/unused` 0, web-Tests 67/477 → 68/481, api 71/1136 → 72/1137, type-check 4/4, lint 5/5. | 2026-09-21 | f85c91b,076ca4b,b92dd5d | [260921-i8x-fehlerverdaechtige-lint-klassen-steuerze](./quick/260921-i8x-fehlerverdaechtige-lint-klassen-steuerze/) |
|
| 260921-i8x | **Fuenf fehlerverdaechtige Lint-Klassen geprueft — kein einziger echter Fehler darunter.** Zwoelf Stellen einzeln beurteilt, Ergebnis: 7x gleichwertig oder Absicht, 2x Haertung, 3x idiomatisch korrekt. Das ist das Ergebnis, keine Ausrede — die Klassen klangen gefaehrlicher als sie waren. **Die eine Stelle mit echtem Wert:** `apps/web/src/lib/safe-next.ts`, der Schutz gegen Weiterleitung auf fremde Seiten nach der Anmeldung. Der Kommentar dort behauptete, die Steuerzeichen stuenden als Unicode-Escapes im Muster; die rohen Bytes zeigten das Gegenteil (NUL, 0x1F, 0x7F direkt eingebettet). Funktionierte, war aber zerbrechlich: verschluckt ein Werkzeug das NUL-Byte, wird aus dem Bereich stillschweigend ein anderer und der Schutz loechrig — der Rueckgabewert landet in `login/page.tsx` direkt in `window.location.href`. Jetzt echte Escapes, Datei ohne ein einziges Steuerbyte. **Beweis der Gleichwertigkeit, nicht Behauptung:** ueber alle 65536 Codepunkte dieselbe Menge abgelehnter Zeichen — 54 Stueck (32 Steuerzeichen 0x00-0x1F, dazu 0x7F, Backslash und die 20 Leerraum-Zeichen von `\s`), null Abweichung; vom Orchestrator unabhaengig gegen ein selbst gebautes Referenzmuster nachgerechnet. **Bewusst nicht angefasst:** die NUL-Maskierung in `ldap.service.ts` (RFC 4515) — genau dieses Zeichen zu treffen ist ihr Zweck, wer sie "repariert", oeffnet LDAP-Filter-Injection. Ebenso die drei `while ((m = re.exec(s)))`-Schleifen (idiomatisch, kein verrutschtes Gleichheitszeichen) und `noUselessSwitchCase` aus bi2. **Zwei Korrekturen an frueheren Annahmen:** `sanitizeNextPath` laeuft NICHT in der Edge-Middleware (die importiert nur `buildNextParam`), und das blosse Umschreiben auf Escapes senkt die Warnzahl nicht — Biome beanstandet die Escape-Schreibweise genauso, es braucht zusaetzlich einen einzeiligen Unterdrueckungskommentar. **Werkzeugfalle, dreimal zugeschnappt:** das Schreibwerkzeug wandelt `\uXXXX` still in das echte Zeichen um — der Planer erzeugte so zehn rohe Steuerbytes in seiner ersten Planfassung, der Executor zweimal in Commit-Text und Akte (git verweigerte den Commit wegen eines NUL-Bytes), und der Orchestrator beim Nachrechnen. Umgehung ueber `python3`/`chr(92)` ist im Plan hinterlegt. **Zahlen:** 446 → 434, `noControlCharactersInRegex`/`useIterableCallbackReturn`/`noGlobalIsNan` je 0, `suppressions/unused` 0, web-Tests 67/477 → 68/481, api 71/1136 → 72/1137, type-check 4/4, lint 5/5. | 2026-09-21 | f85c91b,076ca4b,b92dd5d | [260921-i8x-fehlerverdaechtige-lint-klassen-steuerze](./quick/260921-i8x-fehlerverdaechtige-lint-klassen-steuerze/) |
|
||||||
|
| 260921-iwr | **Listenschluessel und Ausrufezeichen-Zusicherungen: 30 Stellen geprueft, wieder kein echter Fehler.** Damit ist der fehlerverdaechtige Rueckstand abgearbeitet. **Zwei Vorannahmen des Orchestrators widerlegt, beide durch Messung statt Argument:** (1) Die LDAP-Seite galt als heisser Kandidat, weil dort Zuordnungsregeln hinzugefuegt und geloescht werden — die Liste, die tatsaechlich waechst und schrumpft (`config.fieldMappings`), benutzt jedoch laengst `key={mapping.id}`; die sechs Meldungen betreffen zustandslose Textlisten. (2) Im Cert-Manager galt eine Zusicherung auf hochgeladenen Dateiinhalt als moeglicher Absturz — der Planer hat eine 83-Byte-Schrottdatei gebaut, die node-forge `bag.cert = null` setzen laesst, und gegen den echten Dienst laufen lassen: **alle vier Pfade enden mit 400, nie 500**, und `certificateToPem(null)` wirft nachweislich, statt still ein falsches Zertifikat zu bauen. Also weder Verfuegbarkeits- noch Integritaetsluecke, sondern eine irrefuehrende Fehlermeldung. **Ein Fund dreht die Richtung um:** bei `admin/modules/grants/page.tsx:246` waere die Korrektur schaedlich — die Gruppierung fasst nur aufeinanderfolgende Kategorien zusammen, die Positionsnummer ist dort fuer die Eindeutigkeit noetig, ohne sie entstuenden doppelte Schluessel. **Geaendert: 5 Stellen** (drei Waechter im Cert-Manager, die den Meldungstext praezisieren — Status bleibt 400, rot-dann-gruen belegt; zwei ueberfluessige Zusicherungen in `imap.provider.ts`, die imapflow ohnehin als Pflichtfeld typisiert). **25 Stellen bleiben bewusst stehen und bleiben in der Zaehlung sichtbar** — mit Begruendung je Stelle in der Akte, damit der naechste Durchgang sie nicht erneut aufrollt; kein Unterdrueckungskommentar, um die Zahl zu schoenen. **Das Tor hat sich selbst bewaehrt:** der erste Entwurf eines Waechters erzeugte einen neuen Lint-Fund (430 statt 429) und wurde von der Verifikation des Plans gefangen; die Reparatur brach `tsc`, weil `@types/node-forge` `Bag.cert` als `Certificate | undefined` deklariert, waehrend die Bibliothek zur Laufzeit `null` zuweist — Endfassung prueft beides. **Zahlen:** 434 → 429, `noArrayIndexKey` unveraendert 19 (alle geprueft, alle harmlos), `noNonNullAssertion` 11 → 6, web-Tests 68/481 → 69/484, api 72/1137 → 72/1143, type-check 4/4, lint 5/5. | 2026-09-21 | 8716fa5,b4aaed4,27909e4,de69863 | [260921-iwr-listenschluessel-per-positionsnummer-und](./quick/260921-iwr-listenschluessel-per-positionsnummer-und/) |
|
||||||
|
|
||||||
## Deferred Items
|
## Deferred Items
|
||||||
|
|
||||||
@@ -495,4 +496,4 @@ Last session: 2026-09-21T04:50:00Z
|
|||||||
Resumed: 2026-09-21 — Sitzung ueber /gsd-resume-work fortgesetzt. Stand geprueft: Arbeitsbaum sauber, main == origin/main auf 55aa287, CI-Lauf 387 fuer 55aa287 erfolgreich (Beta-Images gebaut). Push und CI aus dem letzten Stopp-Punkt sind damit erledigt.
|
Resumed: 2026-09-21 — Sitzung ueber /gsd-resume-work fortgesetzt. Stand geprueft: Arbeitsbaum sauber, main == origin/main auf 55aa287, CI-Lauf 387 fuer 55aa287 erfolgreich (Beta-Images gebaut). Push und CI aus dem letzten Stopp-Punkt sind damit erledigt.
|
||||||
Stopped at: Warte auf Nutzerentscheidung, womit weitergearbeitet wird. Offen fuer den User: alpha pullen (web+api) und danach am Windows-VM-Client die echte Fehlermeldung schicken (Betreff `[Desktop/Windows]` + `Herkunft:`-Zeile pruefen); eigenen Arbeitsplatz-Client einmal per Browser-Installer erneuern; Freigabe 1.3.0 auf Zuruf. Technisch offen im Ledger: WINDOWS #35 (Biome laeuft nicht — biome.json:3 `organizeImports` ist in Biome 2.5.0 unbekannt, `biome check` bricht mit Konfigurationsfehler ab, reproduziert 2026-09-21) und WINDOWS #36 (403-Antworten bleiben in handleSubmit/handleDelete ohne sichtbare Reaktion).
|
Stopped at: Warte auf Nutzerentscheidung, womit weitergearbeitet wird. Offen fuer den User: alpha pullen (web+api) und danach am Windows-VM-Client die echte Fehlermeldung schicken (Betreff `[Desktop/Windows]` + `Herkunft:`-Zeile pruefen); eigenen Arbeitsplatz-Client einmal per Browser-Installer erneuern; Freigabe 1.3.0 auf Zuruf. Technisch offen im Ledger: WINDOWS #35 (Biome laeuft nicht — biome.json:3 `organizeImports` ist in Biome 2.5.0 unbekannt, `biome check` bricht mit Konfigurationsfehler ab, reproduziert 2026-09-21) und WINDOWS #36 (403-Antworten bleiben in handleSubmit/handleDelete ohne sichtbare Reaktion).
|
||||||
Resume file: None
|
Resume file: None
|
||||||
Last activity: 2026-09-21 - Quick 260921-i8x: fuenf fehlerverdaechtige Lint-Klassen geprueft, kein echter Fehler darunter; Weiterleitungsschutz safe-next.ts von rohen Steuerbytes auf Escapes gehaertet, Gleichwertigkeit ueber alle 65536 Codepunkte nachgerechnet (54 abgelehnte Zeichen, null Abweichung)
|
Last activity: 2026-09-21 - Quick 260921-iwr: 30 Listenschluessel- und Zusicherungs-Stellen geprueft, kein echter Fehler; beide Verdachtsmomente (LDAP-Seite, Cert-Manager) durch Messung widerlegt. Der fehlerverdaechtige Rueckstand ist damit abgearbeitet
|
||||||
|
|||||||
+265
@@ -0,0 +1,265 @@
|
|||||||
|
---
|
||||||
|
phase: quick-260921-iwr
|
||||||
|
plan: 01
|
||||||
|
subsystem: testing
|
||||||
|
tags: [biome, lint, react, node-forge, pkcs12, imapflow, cert-manager]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires: []
|
||||||
|
provides:
|
||||||
|
- 30 einzeln beurteilte Fundstellen (19 noArrayIndexKey, 11 noNonNullAssertion) mit Urteil und Folgenbegruendung
|
||||||
|
- Drei echte Waechter in cert-manager.service.ts statt sachlich falscher Zusicherungen (bag.cert = null)
|
||||||
|
- Zwei entfernte ueberfluessige Zusicherungen in imap.provider.ts (uid ist Pflichtfeld)
|
||||||
|
- Neue Tests: MergeTab.test.tsx (Entfernen mittig/vorne), Rundenliste in stopwatch-widget.test.tsx, sechs neue Faelle fuer die malformte PFX in cert-manager.service.spec.ts
|
||||||
|
affects: []
|
||||||
|
|
||||||
|
# Actuals (#2632)
|
||||||
|
actuals:
|
||||||
|
tokens: 5300
|
||||||
|
tasks: 3
|
||||||
|
commits: 4
|
||||||
|
plan_head_before: cfba3c953202057064adbe691b735977654f127c
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Malformte PFX-Testdatei per forge.asn1 handgebaut statt Fixture-Datei — exakte Kontrolle ueber den Fehlerfall (bag.cert = null)"
|
||||||
|
- "Zusicherung durch Typpraedikat-Filter ersetzt statt Filtern/Ueberspringen bei fehlendem Zertifikat (D-04)"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- apps/web/src/app/(portal)/modules/cert-manager/components/MergeTab.test.tsx
|
||||||
|
modified:
|
||||||
|
- apps/api/src/cert-manager/cert-manager.service.ts
|
||||||
|
- apps/api/src/cert-manager/cert-manager.service.spec.ts
|
||||||
|
- apps/api/src/inbox/imap.provider.ts
|
||||||
|
- apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx
|
||||||
|
- apps/web/src/components/dashboard/widgets/stopwatch-widget.test.tsx
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Alle 19 noArrayIndexKey-Fundstellen bleiben im Produktivcode unveraendert — zwoelf sind Ladeplatzhalter/Meldungslisten ohne Identitaet (tatsaechlich zehn, siehe Korrektur unten), eine (grants) braucht den Index sachlich, zwei (MergeTab, Stoppuhr) sind jetzt per Test belegt statt nur behauptet."
|
||||||
|
- "Drei bag.cert-Zusicherungen in cert-manager.service.ts sind sachlich falsch (node-forge kann bag.cert auf null setzen), aber keine Verfuegbarkeits- oder Integritaetsluecke: alle vier betroffenen Pfade antworteten schon vorher mit 400, nie 500. Der Eingriff ist Diagnose-/Lesbarkeitsarbeit — die Meldung wird praezise, der Statuscode bleibt 400 (D-02)."
|
||||||
|
- "Zwei uid!-Zusicherungen in imap.provider.ts entfernt: imapflow typisiert uid als Pflichtfeld, das Ausrufezeichen war wirkungslos."
|
||||||
|
- "Drei weitere Zusicherungen (cert-manager 133/516/661, tenders.controller.ts:244, favorites-widget.tsx:149, sidebar.tsx:80) bleiben unveraendert — jede durch eine konkrete vorgelagerte Zeile oder einen strukturellen Fakt garantiert."
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "Handgebaute ASN.1-Testfixtures fuer node-forge-Grenzfaelle (forge.asn1.create) statt auf Zufallsdaten oder Mocks zu setzen"
|
||||||
|
|
||||||
|
requirements-completed: [D-01, D-02, D-03, D-04, D-05, D-06]
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
- id: D1
|
||||||
|
description: "Alle 19 noArrayIndexKey-Fundstellen einzeln beurteilt, MergeTab-Entfernen (mittig + vorne) und Stoppuhr-Rundenreihenfolge per Test belegt"
|
||||||
|
requirement: "D-01"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/web/src/app/(portal)/modules/cert-manager/components/MergeTab.test.tsx#quick-260921-iwr: Entfernen der mittleren Datei laesst genau die erste und dritte Datei uebrig..."
|
||||||
|
status: pass
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/web/src/components/dashboard/widgets/stopwatch-widget.test.tsx#quick-260921-iwr: zwei Runden nacheinander..."
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D2
|
||||||
|
description: "Drei bag.cert-Zusicherungen in cert-manager.service.ts zu echten Waechtern mit praeziser 400-Meldung gemacht (parseCert, mergeCerts, convertCert)"
|
||||||
|
requirement: "D-04"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/api/src/cert-manager/cert-manager.service.spec.ts#PFX mit unlesbarem Zertifikats-Bag (bag.cert = null)"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D3
|
||||||
|
description: "Zwei ueberfluessige Ausrufezeichen in imap.provider.ts entfernt (uid ist imapflow-Pflichtfeld)"
|
||||||
|
requirement: "D-05"
|
||||||
|
verification:
|
||||||
|
- kind: other
|
||||||
|
ref: "cd apps/api && npx tsc --noEmit (exit 0)"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D4
|
||||||
|
description: "Biome-Zaehlbefehl bestaetigt TOTAL 434 -> 429, ARRAYKEY unveraendert 19, NONNULL 11 -> 6, ERRORS 0"
|
||||||
|
requirement: "D-06"
|
||||||
|
verification:
|
||||||
|
- kind: other
|
||||||
|
ref: "npx biome lint --reporter=json . | Zaehlbefehl aus planning_observations"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
|
||||||
|
duration: 20min
|
||||||
|
completed: 2026-09-21
|
||||||
|
status: complete
|
||||||
|
---
|
||||||
|
|
||||||
|
# Quick 260921-iwr: Listenschluessel per Positionsnummer und Ausrufezeichen-Zusicherungen Summary
|
||||||
|
|
||||||
|
**30 Fundstellen zweier Lint-Klassen einzeln beurteilt — 5 geaenderte Stellen (3 echte Waechter in cert-manager.service.ts, 2 ueberfluessige Zusicherungen in imap.provider.ts entfernt), 25 bewusst stehengelassen, zwei bislang nur behauptete Faelle jetzt per Test belegt.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** ~20 min
|
||||||
|
- **Tasks:** 3
|
||||||
|
- **Files modified:** 6 (5 geaendert, 1 neu angelegt)
|
||||||
|
- **Commits:** 4 (3 Task-Commits + 1 Nachbesserung, siehe Deviations)
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Alle 19 `noArrayIndexKey`-Fundstellen beurteilt: zehn Ladeplatzhalter mit fester Laenge, sechs zustandslose Meldungslisten in admin/ldap, eine Fundstelle (grants), bei der der Index sachlich notwendig ist, und zwei echte Faelle (MergeTab, Stoppuhr-Runden), die jetzt per Test statt nur per Behauptung abgesichert sind.
|
||||||
|
- Alle 11 `noNonNullAssertion`-Fundstellen beurteilt: drei sind durch node-forges eigenes Verhalten sachlich falsch (bag.cert kann null sein) und wurden zu echten Waechtern mit praeziser 400-Meldung; zwei sind schlicht ueberfluessig (imapflow typisiert `uid` als Pflichtfeld) und wurden entfernt; sechs sind durch eine konkrete Zeile oder einen strukturellen Fakt garantiert und bleiben unveraendert.
|
||||||
|
- Neue `MergeTab.test.tsx` belegt: Entfernen der mittleren sowie der ersten Datei aus einer Dreier- bzw. Zweierliste laesst die verbleibenden Dateien mit ihrem eigenen Namen und eigenen Entfernen-Knopf zurueck — kein Verwechseln durch den Positionsschluessel.
|
||||||
|
- Neuer Testfall in `stopwatch-widget.test.tsx` belegt: zwei Runden nacheinander zeigen die neuere Runde oben mit Rundennummer 2, die aeltere unten mit Rundennummer 1, jede Zeit bei ihrer eigenen Nummer.
|
||||||
|
- Sechs neue Testfaelle in `cert-manager.service.spec.ts`, RED zuerst geschrieben (heutige Meldungen bestaetigt), dann GREEN nach Einbau der drei Waechter — inklusive einer selbst mit `forge.asn1` gebauten 83-Byte-PFX-Datei mit unlesbarem Zertifikats-Bag und einer Vorbedingungspruefung, dass diese Datei tatsaechlich `bag.cert = null` erzeugt.
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Jede Aufgabe wurde atomar committet:
|
||||||
|
|
||||||
|
1. **Task 1: Die 19 Positionsschluessel beurteilen und die zwei nicht offensichtlichen Faelle nachmessen** - `8716fa5` (feat)
|
||||||
|
2. **Task 2: Die sechs Zusicherungen in cert-manager.service.ts beurteilen, drei davon zu echten Waechtern machen** - `b4aaed4` (fix, TDD: RED zuerst, dann GREEN)
|
||||||
|
3. **Task 3: Die restlichen fuenf Zusicherungen beurteilen, zwei ueberfluessige entfernen** - `27909e4` (refactor)
|
||||||
|
|
||||||
|
**Nachbesserung (Deviation, siehe unten):** `de69863` (fix) — behebt einen durch Task 2 selbst eingefuehrten neuen Lint-Fund, der die Endverifikation (TOTAL=429) verfehlt haette.
|
||||||
|
|
||||||
|
**Plan-Metadaten:** wird vom Orchestrator committet (SUMMARY.md, STATE.md, ROADMAP.md).
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `apps/api/src/cert-manager/cert-manager.service.ts` - drei bag.cert-Zusicherungen durch echte Waechter mit praeziser 400-Meldung ersetzt
|
||||||
|
- `apps/api/src/cert-manager/cert-manager.service.spec.ts` - neue Testgruppe fuer die malformte PFX (Vorbedingung + vier Pfade + Regressionstest fuer eine gueltige PFX)
|
||||||
|
- `apps/api/src/inbox/imap.provider.ts` - zwei ueberfluessige `msg.uid!` zu `msg.uid` vereinfacht
|
||||||
|
- `apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx` - wirkungslose eslint-disable-Zeile durch Sachhinweis ersetzt, kein Schluessel geaendert
|
||||||
|
- `apps/web/src/components/dashboard/widgets/stopwatch-widget.test.tsx` - neuer Testfall fuer zwei aufeinanderfolgende Runden
|
||||||
|
- `apps/web/src/app/(portal)/modules/cert-manager/components/MergeTab.test.tsx` - neu angelegt, zwei Testfaelle fuer das Entfernen aus der Dateiliste
|
||||||
|
|
||||||
|
## Die 30 Fundstellen — vollstaendiges Urteil
|
||||||
|
|
||||||
|
Urteil-Spalte: **Lesbarkeit** = geaendert (Meldung praezisiert bzw. ueberfluessige Zusicherung entfernt, Verhalten sonst unveraendert). **harmlos** = bewusst unveraendert stehengelassen, mit Begruendung.
|
||||||
|
|
||||||
|
### noArrayIndexKey (19 — alle unveraendert im Produktivcode, ARRAYKEY bleibt 19)
|
||||||
|
|
||||||
|
| # | Datei | Zeile | Regel | Urteil | Begruendung |
|
||||||
|
|---|-------|-------|-------|--------|--------------|
|
||||||
|
| 1 | InvoiceHistoryTable.tsx | 129 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:5})`, feste Laenge, keine Identitaet, Reihenfolge aendert sich nie |
|
||||||
|
| 2 | InvoiceHistoryTable.tsx | 131 | noArrayIndexKey | harmlos | Verschachtelter Platzhalter, `Array.from({length:6})`, gleiche Begruendung |
|
||||||
|
| 3 | VehicleTable.tsx | 342 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:3})`, feste Laenge |
|
||||||
|
| 4 | VehicleTable.tsx | 344 | noArrayIndexKey | harmlos | Verschachtelter Platzhalter, `Array.from({length:5})` |
|
||||||
|
| 5 | ResultsList.tsx | 240 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:5})` |
|
||||||
|
| 6 | ResultsList.tsx | 242 | noArrayIndexKey | harmlos | Verschachtelter Platzhalter, `Array.from({length:6})` |
|
||||||
|
| 7 | InboxConfigForm.tsx | 251 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:6})`, Formularfelder ohne Inhalt |
|
||||||
|
| 8 | EmailAlertConfigForm.tsx | 225 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:5})` |
|
||||||
|
| 9 | RssFeedListForm.tsx | 151 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:2})` |
|
||||||
|
| 10 | SourceConfigForm.tsx | 129 | noArrayIndexKey | harmlos | Ladeplatzhalter, `Array.from({length:2})` |
|
||||||
|
| 11 | admin/ldap/page.tsx | 1040 | noArrayIndexKey | harmlos | `nameCollisions`-Meldungsliste; `setGroupImportResult(data)` ersetzt das ganze Ergebnis auf einen Schlag, keine Zeile bleibt haengen |
|
||||||
|
| 12 | admin/ldap/page.tsx | 1049 | noArrayIndexKey | harmlos | `errors`-Meldungsliste im Gruppenimport, gleicher Ersetz-Mechanismus |
|
||||||
|
| 13 | admin/ldap/page.tsx | 1369 | noArrayIndexKey | harmlos | `emailConflicts`-Meldungsliste; `setSyncResult(null)` dann `setSyncResult(data)` — komplettes Ersetzen |
|
||||||
|
| 14 | admin/ldap/page.tsx | 1384 | noArrayIndexKey | harmlos | `skippedNoLogin`-Meldungsliste, gleicher Mechanismus |
|
||||||
|
| 15 | admin/ldap/page.tsx | 1396 | noArrayIndexKey | harmlos | `entryFailures`-Meldungsliste, gleicher Mechanismus |
|
||||||
|
| 16 | admin/ldap/page.tsx | 1405 | noArrayIndexKey | harmlos | `errors`-Meldungsliste im Sync-Ergebnis, gleicher Mechanismus |
|
||||||
|
| 17 | admin/modules/grants/page.tsx | 246 | noArrayIndexKey | harmlos | `Fragment key={cat-${category}-${groupIndex}}` — die Gruppierung fasst nur unmittelbar aufeinanderfolgende Module gleicher Kategorie zusammen, dieselbe Kategorie kann also mehrfach vorkommen; der Positionsanteil ist notwendig, Entfernen wuerde doppelte Schluessel erzeugen. Die Haken selbst haengen an `cellKey(mod.id, g.id)`, nicht an diesem Schluessel |
|
||||||
|
| 18 | MergeTab.tsx | 87 | noArrayIndexKey | harmlos | `key={\`${f.name}-${i}\`}` — jetzt per Test belegt statt nur behauptet: Entfernen der mittleren/ersten Datei verwechselt keine Zeile (neue MergeTab.test.tsx) |
|
||||||
|
| 19 | stopwatch-widget.tsx | 276 | noArrayIndexKey | harmlos | `key={idx}` — Zeilen halten keinen eigenen Zustand, Rundennummer wird aus Laenge und Position berechnet; jetzt per neuem Testfall belegt (zwei Runden, richtige Zeit bei richtiger Nummer). Wirkungslose eslint-disable-Zeile ersetzt durch Sachhinweis, keine neue Unterdrueckung |
|
||||||
|
|
||||||
|
**Korrektur zur Planvorgabe:** Die planning_observations sprechen von "zwoelf" Ladeplatzhaltern in Gruppe (a); die dort selbst aufgelistete Datei/Zeile-Aufstellung nennt aber nur zehn Stellen (InvoiceHistoryTable x2, VehicleTable x2, ResultsList x2, InboxConfigForm x1, EmailAlertConfigForm x1, RssFeedListForm x1, SourceConfigForm x1 = 10). Zusammen mit den sechs admin/ldap-Meldungslisten, der einen grants-Stelle und den zwei echten Faellen (MergeTab, Stoppuhr) ergeben sich 10+6+1+2 = 19 — die Gesamtzahl stimmt, nur die Zwischensumme der Gruppe (a) war im Plantext falsch benannt. Kein Code-Fund betroffen, reine Dokumentationskorrektur.
|
||||||
|
|
||||||
|
### noNonNullAssertion (11 — 5 geaendert, 6 unveraendert, NONNULL 11 -> 6)
|
||||||
|
|
||||||
|
| # | Datei | Zeile | Regel | Urteil | Begruendung |
|
||||||
|
|---|-------|-------|-------|--------|--------------|
|
||||||
|
| 20 | cert-manager.service.ts | 133 | noNonNullAssertion | harmlos | Zerlegung des Fingerabdruck-Hex in Zweiergruppen — SHA-1/SHA-256-Hex ist immer 40/64 Zeichen lang, `match(/.{2}/g)` findet garantiert etwas. Durch die Hash-Laenge garantiert, nicht durch Eingabedaten |
|
||||||
|
| 21 | cert-manager.service.ts | 207 | noNonNullAssertion | **Lesbarkeit** | `bags[0].cert!` in parseCert — node-forge kann bag.cert auf null setzen (lib/pkcs12.js Zeile 703-709), die Zusicherung war sachlich falsch. Folge war aber bereits behandelt (400, nie 500). Jetzt echter Waechter: praezise BadRequestException statt der irrefuehrenden "Failed to extract certificate details" |
|
||||||
|
| 22 | cert-manager.service.ts | 469 | noNonNullAssertion | **Lesbarkeit** | `bags.map((bag) => bag.cert!)` in mergeCerts — gleicher Grund. Jetzt: alle Bag-Zertifikate werden auf Vollstaendigkeit geprueft (kein Filtern, kein Ueberspringen, D-04), bei fehlendem Zertifikat wirft der Guard unter Nennung des Dateinamens, sonst wird die Liste ueber ein Typpraedikat zurueckgegeben |
|
||||||
|
| 23 | cert-manager.service.ts | 516 | noNonNullAssertion | harmlos | Kennwort beim PFX-Erzeugen in mergeCerts — durch die vorgelagerte Pruefung in Zeile 438-440 garantiert (bricht bei fehlendem/leerem Kennwort vorher mit 400 ab) |
|
||||||
|
| 24 | cert-manager.service.ts | 602 | noNonNullAssertion | **Lesbarkeit** | `bags[0].cert!` in convertCert — gleicher Grund wie #21. Jetzt echter Waechter mit praeziser Meldung statt der irrefuehrenden "Failed to convert certificate to pem: serialization error" |
|
||||||
|
| 25 | cert-manager.service.ts | 661 | noNonNullAssertion | harmlos | Kennwort beim PFX-Erzeugen in convertCert — durch die vorgelagerte Pruefung in Zeile 564-566 garantiert |
|
||||||
|
| 26 | imap.provider.ts | 214 | noNonNullAssertion | **Lesbarkeit** | `msg.uid!` — imapflow typisiert `uid` in `FetchMessageObject` als Pflichtfeld (lib/imap-flow.d.ts Zeile 469, "Always included in the response"). Zusicherung war ueberfluessig, entfernt (nur `!` weg, sonst nichts). tsc bleibt gruen |
|
||||||
|
| 27 | imap.provider.ts | 310 | noNonNullAssertion | **Lesbarkeit** | `msg.uid!` — gleicher Grund wie #26 |
|
||||||
|
| 28 | tenders.controller.ts | 244 | noNonNullAssertion | harmlos | `this.tenderIngestionService!.pollDueSources()` — das Fragezeichen im Konstruktor dient nur der Argumentreihenfolge; `TenderIngestionService` steht in tenders.module.ts unter providers, wird von Nest also immer eingesetzt. Durch den Provider-Eintrag garantiert |
|
||||||
|
| 29 | favorites-widget.tsx | 149 | noNonNullAssertion | harmlos | `byId.get(fid)!` — die Reihenfolge stammt aus `sortedFavorites` (Zeile 90-97, sortierte Kopie von `favorites`), die Zuordnungstabelle wird aus denselben Eintraegen gebaut. Durch die gemeinsame Herleitung garantiert |
|
||||||
|
| 30 | sidebar.tsx | 80 | noNonNullAssertion | harmlos | `categories.get(cat)!.push(mod)` — Zeile 79 legt den Eintrag an, falls er fehlt. Durch die Zeile unmittelbar davor garantiert |
|
||||||
|
|
||||||
|
## Vorher-Nachher-Zahlen (Zaehlbefehl aus den planning_observations, Feld `category`, nie Quelltext)
|
||||||
|
|
||||||
|
| Zaehlwert | Vorher | Nachher | Erwartung laut Plan | Ergebnis |
|
||||||
|
|-----------|--------|---------|----------------------|----------|
|
||||||
|
| TOTAL | 434 | 429 | 429 | ✓ (faellt um genau 5, die Zahl der geaenderten Stellen) |
|
||||||
|
| ERRORS | 0 | 0 | 0 | ✓ |
|
||||||
|
| ARRAYKEY | 19 | 19 | 19 | ✓ unveraendert |
|
||||||
|
| NONNULL | 11 | 6 | 6 | ✓ (5 geaendert: 3 cert-manager + 2 imap) |
|
||||||
|
|
||||||
|
## Verhaltensaenderung (vorab benannt, D-02)
|
||||||
|
|
||||||
|
Die einzige Verhaltensaenderung betrifft eine Eingabeklasse: eine PFX-Datei, deren Zertifikats-Bag wohlgeformt, aber nicht als X.509 lesbar ist. Vorher und nachher antworten alle vier betroffenen Pfade mit **Status 400** — das aendert sich nicht. Was sich aendert, ist ausschliesslich der **Text** der Fehlermeldung:
|
||||||
|
|
||||||
|
| Pfad | Meldung vorher | Meldung nachher |
|
||||||
|
|------|-----------------|-------------------|
|
||||||
|
| parseCert | "Failed to extract certificate details" | "Certificate bag in PFX/PKCS12 does not contain a readable X.509 certificate" |
|
||||||
|
| mergeCerts (pem) | "Failed to create merged certificate output" | "Certificate bag in \"<Dateiname>\" does not contain a readable X.509 certificate" |
|
||||||
|
| mergeCerts (pfx) | "Failed to create merged certificate output" | dieselbe praezise Meldung wie oben |
|
||||||
|
| convertCert | "Failed to convert certificate to pem: serialization error" | "Certificate bag in PFX/PKCS12 does not contain a readable X.509 certificate" |
|
||||||
|
|
||||||
|
Das ist **Diagnose-/Lesbarkeitsarbeit, keine Fehlerbehebung**: node-forge wirft bei einem fehlenden Zertifikat in `certificateToPem` und `toPkcs12Asn1` ausnahmslos, ein still falsches Zertifikat war schon vorher ausgeschlossen. Die drei Zusicherungen waren sachlich falsch, aber ihre Folge war bereits sicher behandelt.
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- Die drei bag.cert-Zusicherungen wurden als "keine Verfuegbarkeits- und keine Integritaetsluecke" eingestuft, nachdem die gemessene Tabelle (400 statt 500 in allen vier Pfaden) und die node-forge-Pruefung (certificateToPem(null)/toPkcs12Asn1(null,...) werfen beide) das belegten. Der Eingriff blieb deshalb auf die Meldung beschraenkt, keine Statuscode- oder Fehlerbehandlungs-Aenderung.
|
||||||
|
- In mergeCerts wurde bewusst kein Filtern/Ueberspringen fehlender Zertifikate implementiert (D-04): stattdessen prueft der Guard alle Bags auf Vollstaendigkeit und wirft bei der ersten Luecke unter Nennung des Dateinamens, erst danach wird die Liste ueber ein Typpraedikat auf den non-null-Typ eingeengt.
|
||||||
|
- Fuer Task 1 wurde keine der 19 Fundstellen im Produktivcode geaendert — stattdessen wurden die zwei einzigen tatsaechlich dynamischen Listen (MergeTab, Stoppuhr) per neuem Test statt nur per Behauptung abgesichert, wie von der Aufgabe gefordert.
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 1 - Bug] mergeCerts-Waechter loeste einen zusaetzlichen Biome-Lint-Fund aus**
|
||||||
|
- **Found during:** Endverifikation nach Task 3 (Schritt "1. `npx biome lint`" aus dem `<verification>`-Block des Plans)
|
||||||
|
- **Issue:** Die erste Fassung des Guards in mergeCerts (`bagCerts.findIndex((c) => c === null)`) erzeugte einen neuen `lint/complexity/useIndexOf`-Fund (info-Severity), der TOTAL auf 430 statt der erwarteten 429 anhob — ein Verstoss gegen D-06 ("TOTAL steigt nirgends anders an"). Ein direkter Fix mit `indexOf(null)` scheiterte an `tsc`, weil `@types/node-forge` `Bag.cert` als `Certificate | undefined` deklariert (nicht `| null`), obwohl die node-forge-Laufzeit selbst `null` setzt.
|
||||||
|
- **Fix:** Die Pruefung testet jetzt ausdruecklich auf `undefined` UND `null` (`c === undefined || c === null`) — das ist fuer Biome kein Single-Value-Vergleich mehr (kein useIndexOf-Vorschlag) und fuer TypeScript typsicher.
|
||||||
|
- **Files modified:** apps/api/src/cert-manager/cert-manager.service.ts
|
||||||
|
- **Verification:** `npx biome lint --reporter=json .` liefert danach TOTAL=429 ERRORS=0 ARRAYKEY=19 NONNULL=6; `tsc --noEmit` exit 0; volle api-Suite weiterhin 72/1143 gruen.
|
||||||
|
- **Committed in:** de69863 (eigener Fix-Commit, da Task 2 bereits committet war, als die Endverifikation dies aufdeckte)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total deviations:** 1 auto-fixed (Rule 1 - Bug, durch die eigene Aenderung eingefuehrt und in der Endverifikation entdeckt)
|
||||||
|
**Impact on plan:** Kein Scope-Creep; die Korrektur war notwendig, um D-06 exakt zu erfuellen (TOTAL=429, nicht 430).
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
Keine ungeloesten Probleme. Die einzige Ueberraschung war die oben dokumentierte Deviation — sie wurde durch den letzten Verifikationsschritt selbst gefangen, bevor sie zur Endabgabe kam.
|
||||||
|
|
||||||
|
## Auth Gates
|
||||||
|
|
||||||
|
Keine — dieser Vorgang hatte keine Authentifizierungs-Interaktion.
|
||||||
|
|
||||||
|
## Known Stubs
|
||||||
|
|
||||||
|
Keine.
|
||||||
|
|
||||||
|
## Threat Flags
|
||||||
|
|
||||||
|
Keine neue sicherheitsrelevante Oberflaeche eingefuehrt. Die drei Waechter in cert-manager.service.ts verschaerfen die Fehlerbehandlung an einer bestehenden Vertrauensgrenze (Datei-Upload, T-iwr-02/T-iwr-03 aus dem Plan-Threat-Model), ohne neue Endpunkte, Auth-Pfade oder Schema-Aenderungen einzufuehren.
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None - keine externe Dienstkonfiguration erforderlich.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
Die letzte Fehlerklasse vor den beiden neuen Dashboard-Widgets ist abgeschlossen: Lint-Rueckstand bei TOTAL=429, ERRORS=0. Keine Blocker fuer die naechsten geplanten Schritte (zwei neue Widgets, laut STATE.md).
|
||||||
|
|
||||||
|
## Self-Check
|
||||||
|
|
||||||
|
- FOUND: apps/api/src/cert-manager/cert-manager.service.ts (geaendert)
|
||||||
|
- FOUND: apps/api/src/cert-manager/cert-manager.service.spec.ts (geaendert)
|
||||||
|
- FOUND: apps/api/src/inbox/imap.provider.ts (geaendert)
|
||||||
|
- FOUND: apps/web/src/components/dashboard/widgets/stopwatch-widget.tsx (geaendert)
|
||||||
|
- FOUND: apps/web/src/components/dashboard/widgets/stopwatch-widget.test.tsx (geaendert)
|
||||||
|
- FOUND: apps/web/src/app/(portal)/modules/cert-manager/components/MergeTab.test.tsx (neu)
|
||||||
|
- FOUND: Commit 8716fa5 (git log --oneline --all)
|
||||||
|
- FOUND: Commit b4aaed4 (git log --oneline --all)
|
||||||
|
- FOUND: Commit 27909e4 (git log --oneline --all)
|
||||||
|
- FOUND: Commit de69863 (git log --oneline --all)
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: quick-260921-iwr*
|
||||||
|
*Completed: 2026-09-21*
|
||||||
Reference in New Issue
Block a user