# Repo-Audit Datum: 2026-05-19 Scope: vollstaendiges Repo Status: solide mit Risiken ## Kurzfazit - Kritischste Themen: persistente HTML-/Script-Injection ueber `innerHTML`, unsichere Markdown-Link-Behandlung, Funktionsabweichungen bei Reset und Task-Erzeugung ## Reproduzierbarkeit Vor dem Audit wurden die verfuegbaren automatisierten Checks ueber `./scripts/verify-repo.sh` ausgefuehrt. ## Findings 1. [High] Persistente HTML-/Script-Injection ueber unescaped User-State Datei: `index.html:1505-1513`, `index.html:1621-1627`, `index.html:2084-2120`, `index.html:2298-2313` Problem: Mehrere Renderer schreiben benutzerkontrollierte Werte direkt per `innerHTML` ins DOM, ohne sie vorher zu escapen, z. B. `task.t`, `idea.text`, Board-Namen und Gruppennamen. Diese Werte kommen aus Inputs, `contenteditable`, `prompt()` und `localStorage`. Warum es zaehlt: Ein Eintrag wie `` wird persistent gespeichert und bei jedem Render erneut ausgefuehrt. Empfehlung: Alle benutzerkontrollierten Texte konsequent mit `escapeHtml()` behandeln oder DOM-Knoten per `textContent`/`createElement` aufbauen statt HTML-Strings zu interpolieren. 2. [High] Markdown-Renderer erlaubt `javascript:`-Links Datei: `index.html:1953-2017`, insbesondere `index.html:2007` Problem: Der Markdown-Parser escaped zwar HTML, uebernimmt aber Link-Ziele ungeprueft in ``. Ein Markdown-Link wie `[x](javascript:alert(1))` bleibt klickbar. Warum es zaehlt: Jede geladene `.md`-Datei kann so aktiven Script-Code hinter einem scheinbar normalen Link verstecken. Empfehlung: Link-Protokolle whitelisten (`http:`, `https:`, ggf. `file:`) und alles andere verwerfen oder als reinen Text rendern. Zusaetzlich `rel="noopener noreferrer"` setzen. 3. [High] `Reset` setzt nicht auf `DEFAULTS` zurueck, sondern nur die Spalten Datei: `SPEC.md:112`, `index.html:1012-1023` Problem: Laut Spec soll `↺ Reset` das aktuelle Board auf `DEFAULTS` zuruecksetzen. Implementiert wird aber nur `cols = deepClone(DEFAULTS[curId].cols)`. Name, Goal, WIP-Limit, Throughput, SLE, Focus und `overview` bleiben erhalten. Warum es zaehlt: Der sichtbare UX-Vertrag stimmt nicht mit dem Verhalten ueberein; ein Reset hinterlaesst versteckte Altzustaende. Empfehlung: Das gesamte Default-Board zurueckkopieren und danach nur `group/color/ring` aus `BOARD_META` wieder anwenden. 4. [Medium] Aus Ideen erzeugte Tasks bekommen kein `movedAt` Datei: `SPEC.md:137-138`, `index.html:1343-1349`, `index.html:2252-2260` Problem: Beim Droppen einer Idee auf ein Board oder beim Promote-Dialog wird nur `{ t: idea.text }` angelegt. `movedAt` fehlt, obwohl es laut Datenmodell Grundlage fuer Aging ist. Warum es zaehlt: Diese Tasks altern nie sichtbar und verfaelschen die Board-Signale gegenueber regulaer angelegten Karten. Empfehlung: Beim Erzeugen aus Ideen dieselbe Initialisierung wie in `addCard()` verwenden, inklusive `movedAt = Date.now()`. 5. [Medium] "Maskierte" Secrets liegen im Klartext in `localStorage` und im DOM Datei: `SPEC.md:52`, `index.html:906-964`, `index.html:1740-1746` Problem: Der Secrets-Bereich ist nur optisch maskiert. Die Werte werden vollstaendig in `kanban_v2` gespeichert und zusaetzlich als `data-value`/`data-copy` ins DOM geschrieben. Warum es zaehlt: Bei XSS, gemeinsam genutztem Browser-Profil oder lokalem Zugriff sind die Daten sofort auslesbar; die UI suggeriert mehr Schutz als tatsaechlich vorhanden ist. Empfehlung: Keine echten Secrets in App-State speichern. Stattdessen nur Env-Referenzen/Keys-Namen anzeigen oder Secrets separat ausserhalb des Boards halten. 6. [Medium] State-Laden ist unvalidiert und schluckt Fehler vollstaendig Datei: `index.html:855-856`, `index.html:863-864`, `index.html:905-939`, `index.html:2202-2203` Problem: `localStorage`-Payloads werden ohne Shape-Validierung uebernommen, und Exceptions werden mit leeren `catch {}`/`catch(e) {}` verschluckt. Schon eine kaputte Struktur kann das Laden teilweise abbrechen, ohne Hinweis fuer den Nutzer. Warum es zaehlt: Beschaedigter oder zukuenftiger inkompatibler State fuehrt zu stillen Datenverlusten oder schwer erklaerbaren Resets. Empfehlung: Geladene Daten vor der Uebernahme validieren, defekte Teile gezielt verwerfen und einen sichtbaren Recovery-Hinweis anzeigen. ## Test- und Pruefstand - Tests: nicht ausgefuehrt, kein Test-Setup im Repo vorhanden - Lint/Typecheck/Build: nicht ausgefuehrt, kein entsprechendes Setup vorhanden - Shell-Syntax: `bash -n server.sh` erfolgreich - Nicht verifiziert: echtes Browser-Laufzeitverhalten, Drag-and-drop im UI, Verhalten unter beschaedigtem `localStorage` ## Empfohlene Reihenfolge 1. XSS-Pfade in Renderern schliessen 2. Markdown-Link-Whitelist ergaenzen 3. `resetCurrentBoard()` auf echten Default-Reset umstellen 4. Task-Erzeugung vereinheitlichen (`addCard`, Idea-Drop, Promote) 5. Secrets-Handling entschaerfen 6. `localStorage`-Load validieren und Fehler sichtbar machen