diff --git a/tasks/lessons.md b/tasks/lessons.md index d95aaa4..493472a 100644 --- a/tasks/lessons.md +++ b/tasks/lessons.md @@ -116,3 +116,10 @@ **Warum der erste E2E es verfehlte (der Diskriminator):** `server_health` ruft `getQueueState({includeJobs:false})` → kein Job-Error je serialisiert; UND das Fixture nutzte `apiKey=` als Queue-Error → value-scrub fing es eh, auch ohne pattern-scrub. ZWEI Zufälle versteckten den Leak. Der direkte `get_queue_state{includeJobs:true}`-Pfad mit NICHT-Config-Token war nie getrieben. **Regel:** Bei einem Redaktions-Gate (a) jeden Collector EINZELN mit seinen DEFAULT-Args treiben, nicht nur den Aggregat-Hub, der bequeme Flags setzt; (b) Fixtures MÜSSEN ein NICHT-Config-Secret enthalten (opaque Token, der nur als Shape erkennbar ist), sonst testet man value-scrub und glaubt, pattern-scrub zu testen; (c) ein grüner E2E heißt nicht „dicht" — Advisor/Review über jeden Pfad laufen lassen, der einen Freitext-Fehlerstring serialisiert. **Wie anwenden:** Denylist-Redaktion kann einen bare opaque String OHNE `key=`/`Bearer`/URL-Kontext NICHT scrubben — das ist inhärent, kein Defekt. Commit-/Release-Notes ehrlich halten („common secret shapes pattern-scrubbed", nicht „kein Secret verlässt je die Box"). Default-Bind 127.0.0.1 erzwingen (`_safeDiagBindAddress`), 0.0.0.0-Option erst wenn wss/TLS existiert — nie plaintext ws:// auf allen Interfaces. + +## 2026-06-19 — Ein Diagnose-Tool darf NIE den Prozess einfrieren, den es diagnostiziert (v3.3.85) +**Kontext:** Nach Release v3.3.84 das Diagnose-System "intensiv durchtesten". Drei Werkzeuge gebaut: (1) Live-Integration-Harness, das den ECHTEN Gateway-MCP-Prozess (StdioClientTransport) gegen einen echten in-process Agent fährt und ALLE 14 Tools durchprüft; (2) adversariale Redaktions-/Abuse-Probe; (3) unabhängiger code-reviewer-Audit-Subagent. Parallel laufen lassen. +**Bug 1 (Probe + Audit, REAL DoS):** `read_log` kompilierte den vom Client gelieferten `grep` zu `new RegExp(grep,'i')` und lief synchron über bis zu 1 MB Log-Tail — IM Electron-Main-Prozess. `(a+)+$` gegen eine lange Zeile = katastrophisches Backtracking → ganze App friert ein (empirisch: 8s-Timeout, gekillt). JS-Regex ist synchron und nicht abbrechbar → der einzige sichere Fix ist KEINE User-Regex: grep ist jetzt case-insensitive Substring-Filter mit `|`-Alternation. Provably linear. +**Bug 2 (Probe, Whitelist-Integrität):** Die Op-Tabelle war ein Plain-Object-Literal → `OPS['constructor']`/`['toString']`/`['valueOf']` lösen geerbte Object.prototype-Funktionen auf, bestehen `typeof fn==='function'` und liefern `{ok:true}`. Harmlos (kein Secret/Write), aber Whitelist-Loch. Fix: `typeof op==='string' && Object.prototype.hasOwnProperty.call(OPS,op)`. +**Bug 3 (nur Live-Integration sichtbar):** Der Gateway las die App-Version aus `info.data.version`, der Collector liefert sie aber als `info.data.app.version` — der "connected to vX.Y.Z"-Hinweis war still leer. Unit-Tests mit Stubs fingen das NIE; nur das Fahren des echten Gateway-Prozesses gegen den echten Collector deckte die Shape-Diskrepanz auf. +**Regeln:** (1) Ein read-only Diagnose-Agent, der IM Zielprozess läuft, darf keine vom Client kontrollierte Synchron-Operation mit unbegrenztem Aufwand ausführen (Regex, JSON.parse von Riesen-Payloads, etc.) — sonst DoS der diagnostizierten App. Literal-Match statt Regex; alles clampen. (2) Whitelist NIE als Plain-Object mit `obj[key]`-Lookup — Prototype-Member lecken; Set/Map/null-proto/hasOwnProperty. (3) Eine Live-Integration gegen den ECHTEN Out-of-Process-Consumer findet Shape-/Contract-Mismatches, die Mock-Unit-Tests strukturell nicht sehen — bei jedem Protokoll-Grenzübergang (MCP-Tool↔Collector) mindestens EINEN echten End-to-End-Lauf. (4) Ein grüner Custom-E2E ist kein Freibrief: der Advisor/Audit fand den Queue-Leak NACH grünem E2E, weil das Gate bequeme Flags (includeJobs:false) setzte — jeden Collector mit DEFAULT-Args fahren.