Erster Schritt der Aufteilung von WeeklyView.tsx (D1). Die sieben gemeinsamen Typen lagen in der Komponentendatei; SettingsSidebar und GridTaskBlock importierten sie von dort und zogen sich damit die gesamte 11.900-Zeilen-Komponente in ihren Abhaengigkeitsgraphen. WeeklyView.tsx re-exportiert die Typen weiterhin, bestehende Importe bleiben also unveraendert -- kein Anfassen der beiden Importeure noetig. 11.906 -> 11.830 Zeilen. Mit npm run build verifiziert: Exit 0, 36 Seiten generiert, /tasks unveraendert bei 265 kB. v1.117.5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
15 KiB
To-do — Repository-Audit vom 2026-09-06
Abarbeitungsliste aus dem Repository-Audit. Reihenfolge ist bewusst gewählt: schnelle Sicherheitsgewinne zuerst, das große Refactoring zuletzt.
Abhaken mit [x]. Die Nummern in Klammern entsprechen den Punkten aus dem Audit.
Block A — Schnell erledigt, sofortiger Gewinn
A1. Tote Mock-Login-Route entfernen (Audit 3)
src/app/api/auth/login/route.tslöschen- Prüfen, dass niemand sie aufruft (
grep -rn "api/auth/login" src/) - Commit + Version bumpen
Warum: Altcode aus der Anfangszeit. Vergleicht gegen ein hartcodiertes Fake-Passwort
($2b$10$examplehashedpassword), erzeugt ein bedeutungsloses mock-jwt-token-… und setzt es
als Cookie. Weil die Middleware /api/auth/* bewusst durchlässt, ist die Route ohne Login
von außen erreichbar und schreibt jede probierte E-Mail per console.log ins Log.
Kein Login-Bypass (der Vergleich schlägt immer fehl), aber unnötige Angriffsfläche.
A2. npm test reparieren (Audit 6)
- Entscheiden: Jest raus oder Jest einrichten
package.json:"test"auf das zeigen lassen, was wirklich existiert (Playwright)"test:e2e"für Playwright ergänzen- Debug-Skripte in
tests/aussortieren (siehe unten)
Warum: "test": "jest" — aber es gibt keine Jest-Config und keinen einzigen Jest-Test.
Die 15 Dateien in tests/ sind Playwright-Specs, und die Namen verraten den Charakter:
login-debug.spec.ts, css-debug-test.spec.ts, login-simple-debug.spec.ts. Wegwerf-Skripte,
keine Regressionstests. npm test läuft ins Leere.
Aufräumen: Die reinen Debug-Skripte löschen, die brauchbaren behalten und benennen.
A3. dev-Branch auflösen (Audit 5)
- Die vier Maßnahmen aus
f6c4a21frisch aufmainanwenden (siehe A1, B1, B2) devaufmainzurücksetzen oder löschenCLAUDE.md: Branch-Strategie an die gelebte Praxis anpassen
Warum: Commit f6c4a21 vom 04.07.2026 behebt bereits Rate-Limiting, Token-Hashing,
die Mock-Route und die Secret-Datei — wurde aber nie gemerged. dev fehlen inzwischen
70 Commits von main, ein Merge würde alte Dateistände zurückholen. Deshalb:
Commit als Vorlage nehmen, Änderungen neu anwenden, dev danach entsorgen.
CLAUDE.md beschreibt einen dev → main-Workflow, der faktisch nicht gelebt wird
(alles läuft direkt auf main). Doku an die Realität anpassen ist ehrlicher.
Block B — Sicherheit
B1. Rate-Limiting auf den Auth-Endpunkten (Audit 4)
- Sliding-Window-Limiter in
src/lib/rate-limit.tsanlegen - Einbinden in
signup - Einbinden in
resend-verification - Einbinden in
reset-password - Einbinden in
verify-email - Limits dokumentieren (Vorschlag: 5 Versuche / 15 Minuten je IP)
Warum: Im gesamten src/ ist kein Limiter vorhanden. Signup, Passwort-Reset und
Verification-Mails sind unbegrenzt aufrufbar — Einladung zum Missbrauch deines SMTP-Zugangs.
B2. Passwort-Reset-Token nur als SHA-256-Hash speichern (Audit 4)
- Beim Erzeugen: Klartext per Mail, Hash in die DB
- Beim Einlösen: eingehenden Token hashen, dann vergleichen
- Migration für bestehende Tokens (einfachster Weg: alle invalidieren)
Warum: src/app/api/auth/reset-password/route.ts:28 legt passwordResetToken im
Klartext ab. Wer Lesezugriff auf die Datenbank hat, kann fremde Konten übernehmen.
Der Token selbst kommt aus crypto.randomUUID() — das ist in Ordnung, nur die Ablage nicht.
B3. Secrets aus dem Git entfernen und rotieren (Audit 2)
.env.production.saveund.env.localgelöscht (mitshredüberschrieben).gitignoreauf.env*erweitert (Ausnahme.env.example)NEXTAUTH_SECRETneu erzeugt — 44 Zeichen, geprüftSMTP_PASSWORDneu gesetztdeployment-notes.mdundINFRASTRUCTURE_REQUIREMENTS.mdaus dem Index entfernt- Klartext-Werte in beiden Dateien durch Platzhalter ersetzt
- Passwort aus
tests/debug/db-connection-test.spec.tsentfernt - Nach dem Wechsel neu eingeloggt
- Entscheiden: Historie umschreiben oder belassen (Empfehlung: belassen)
- Prüfen, ob eines der geleakten DB-Passwörter noch aktiv ist (siehe F1)
Warum: .env.production.save ist eingecheckt und enthält echte Werte —
DATABASE_URL (70 Zeichen), NEXTAUTH_SECRET (20 Zeichen), SMTP_PASSWORD (20 Zeichen).
.gitignore listet .env.production, die .save-Endung fällt durchs Raster.
Drin seit Commit da8d019.
Entwarnung: Das GitHub-Repo ist privat — nicht öffentlich abgreifbar. Die Werte stehen aber in der kompletten Historie, in jedem Klon und auf Gitea.
Anleitung siehe docs/secret-rotation.md.
Block C — Build und Typsicherheit
C1. Build darf Fehler nicht mehr verschlucken (Audit 1)
- Schritt 1:
eslint.ignoreDuringBuildsinnext.config.jsauffalse - Gemeldete ESLint-Fehler abarbeiten — es waren nur 2 (
prefer-constinSettingsSidebar.tsx), verifiziert:next lintendet jetzt mit Exit 0 - Schritt 2:
npm run type-checklaufen lassen, Fehlerzahl notieren - Fehler abarbeiten — modulweise, nicht auf einmal
- Schritt 3:
typescript.ignoreBuildErrorsauffalse - 3 Dateien mit
@ts-nocheckaufspüren und entschärfen — die überspringen den Typecheck vollständig,ignoreBuildErrors: falsewürde sie gar nicht erfassen any-Stellen reduzieren — ESLint zählt 470no-explicit-any— fortlaufend, kein Blocker- 209 ungenutzte Variablen (
no-unused-vars) aufräumen — fortlaufend - 11 unvollständige Hook-Abhängigkeiten (
react-hooks/exhaustive-deps) prüfen — die sind die gefährlichsten der Warnungen, weil daraus echte Bugs entstehen
Warum: next.config.js hat ignoreDuringBuilds: true und ignoreBuildErrors: true.
tsconfig.json sagt zwar strict: true, das ist aber wirkungslos, weil der Build die Fehler
gar nicht meldet. Ein Typfehler fällt nicht beim Bauen auf, sondern erst als kaputte Seite
im Browser. Ein erfolgreicher Build ist derzeit kein Beweis, dass der Code korrekt ist.
Achtung Reihenfolge: Erst ESLint, dann TypeScript. Beides gleichzeitig abzuschalten blockiert jeden Deploy, bis alles behoben ist.
Block D — Struktur
D1. Dateien über 4000 Zeilen aufteilen (Audit 7)
Betroffen sind genau zwei Dateien:
| Datei | Zeilen |
|---|---|
src/components/WeeklyView.tsx |
11.830 (war 11.906) |
src/components/SettingsSidebar.tsx |
5.445 |
Voraussetzung: C1 muss laufen — ohne Typecheck ist ein Refactoring dieser Größe Blindflug. Ebenso hilfreich: A2, damit es überhaupt Tests gibt.
WeeklyView.tsx → Zielstruktur
- Typen nach
src/lib/weekly/types.ts(7 Typen, 97 Zeilen) — Build verifiziert src/components/weekly/anlegen- Reine Helfer nach
src/lib/weekly/(Datum, Slots, Formatierung) - Drag & Drop in einen Hook
useTaskDragDrop.ts - Someday-Listen in
SomedayLists.tsx - All-Day-Leiste in
AllDayBar.tsx - Zeitraster + Tagesspalten in
TimeGrid.tsx/DayColumn.tsx - Einstellungs-Flyouts in
QuickSettingsPanel.tsx - Daten-Laden in
useWeeklyData.ts WeeklyView.tsxbleibt als Orchestrator übrig
SettingsSidebar.tsx → Zielstruktur
src/components/settings/anlegen- Ein Panel pro Themenbereich als eigene Datei
- Gemeinsame Bausteine (Farbwähler, Schalterzeile) in
settings/controls/
Vorgehen: In kleinen Schritten, jeder Schritt ein eigener Commit, nach jedem Schritt Typecheck. Kein "großer Wurf" — die Datei ist zu zentral.
Warum: WeeklyView.tsx ist ein Viertel des gesamten Quellcodes. Der Weekday-Bug von
Anfang September saß genau dort und war nur deshalb schwer zu finden.
Block E — Dauerhafte Absicherung
E1. Regelmäßige automatische Prüfung (Audit 8)
- SonarQube-Analyse gegen https://sq.martin-bierschenk.de einrichten
- Regelmäßigen Lauf aufsetzen — nightly 03:00,
todo-sonar.timer(systemd user unit) npm auditin denselben Lauf aufnehmen (scripts/quality-check.sh)todo-sonar.timeraufscripts/quality-check.shumstellen statt nurtodo-sonar-scan- Playwright-Tests in den nächtlichen Lauf aufnehmen (braucht laufende App — erst nach A2)
- Quality Gate im SonarQube definieren
- Ergebnis irgendwohin melden, wo es auffällt (bisher nur Konsolenausgabe)
- Entscheiden:
.github/workflows/sonarqube.ymllöschen oder behalten
Stand: Die Analyse läuft bereits — nächtlich um 03:00 auf dem Spark, per systemd-Timer,
Ergebnis auf https://sq.martin-bierschenk.de. Was fehlte, war npm audit (Sicherheitslücken
in Abhängigkeiten findet SonarQube nicht) und ein Bündel-Skript. Beides ist jetzt da.
Zur Entscheidung: GitHub Actions wurde am 02.09. bewusst verworfen — das Repo ist privat,
Actions-Minuten kosten, und der SonarQube-Server läuft ohnehin selbst gehostet. Trotzdem liegt
seit dem 03.09. ein Workflow im Repo, der bei jedem Push auf main anspringt. Er ruft
npm run type-check und npm run lint auf — beides schlägt derzeit fehl (siehe C1), der
Workflow wäre also dauerhaft rot. Sein Projekt-Key war zudem falsch und hätte in ein zweites,
leeres SonarQube-Projekt geschrieben; das ist korrigiert. Entweder löschen (passt zur
Entscheidung vom 02.09.) oder bewusst behalten und C1 vorziehen.
Warum: Bisher fallen Regressionen erst in Produktion auf. In Kombination mit C1 (Build meldet Fehler) und A2 (Tests laufen) entsteht daraus ein echtes Netz.
E2. Abhängigkeiten auf Sicherheitslücken prüfen
npm auditauf dem Container ausführen- Kritische Funde beheben
Warum: Konnte im Audit nicht geprüft werden — node_modules ist auf dem
CIFS-Share nicht installierbar (keine Symlinks). Zu bekannten Lücken in den
Abhängigkeiten liegt daher keine Aussage vor. Muss nachgeholt werden.
Block F — Zurückgestellt
F1. Datenbank-Passwort rotieren (bewusst nach hinten geschoben)
- Prüfen, ob eines der geleakten Passwörter noch aktiv ist
- Falls ja: rotieren nach
docs/secret-rotation.md, Schritt 2 - Falls nein: nichts zu tun, Haken dran
Warum zurückgestellt: Von den drei Zugangsdaten ist dieses das am wenigsten dringende.
Die Datenbank hört auf einer privaten LAN-Adresse, nicht am Internet. Wer das Passwort
aus dem Git holen will, braucht Zugriff auf das private Repository — und ist damit
ohnehin schon im Netz. NEXTAUTH_SECRET und SMTP_PASSWORD waren die kritischen, beide
sind erledigt.
Und es ist riskant: Supabase läuft selbst gehostet per Docker. Dasselbe Passwort steckt
in supabase/docker/.env und in Postgres. Ein halb durchgeführter Wechsel legt die
Datenbank und damit die App lahm. Das gehört an einen ruhigen Abend, nicht nebenbei.
Zwei verschiedene Passwörter liegen im Git (Fingerabdrücke, keine Klartexte):
| Fingerabdruck | Wo | Stand |
|---|---|---|
59fb583eaae9 |
ehem. deployment-notes.md, INFRASTRUCTURE_REQUIREMENTS.md |
altes Docker-Compose-Setup (@db:5432); Dateien bereinigt |
9dbb40b14c4d |
ehem. .env.production.save |
Supabase-Zugang, Status offen — Datei gelöscht, Wert steht noch in der Historie |
Vom Nutzer bestätigt: das alte NEXTAUTH_SECRET (20 Zeichen) und das alte SMTP_PASSWORD
sind nicht mehr in Verwendung, beide wurden rotiert. Offen ist allein das
Datenbank-Passwort.
Prüfen lässt sich das auf dem Container, ohne einen Wert auszugeben:
cd /root/My-Weekly-ToDo-List
grep '^DATABASE_URL=' .env | sed -E 's|.*://[^:]+:([^@]+)@.*|\1|' | tr -d '\n' | sha256sum | cut -c1-12
Ergibt der Befehl 9dbb40b14c4d, ist das geleakte Passwort noch in Benutzung — dann lohnt
F1. Kommt etwas anderes heraus, wurde es längst getauscht und du kannst F1 abhaken.
Block G — Infrastruktur (Supabase-LXC 201)
G1. Container läuft privilegiert (Sicherheitsthema)
- Entscheiden, ob
unprivileged: 0so bleiben soll - Falls nicht: Container neu aufsetzen und Daten übernehmen
Warum: pct config 201 meldet unprivileged: 0. Wer aus diesem Container ausbricht, ist
root auf dem NUC — bei einem Container, der die Datenbank betreibt, die relevantere
Baustelle als ein fehlendes Root-Passwort. Meist wird das umgestellt, weil Docker im
unprivilegierten Container zickt; nachvollziehbar, aber ein bewusster Kompromiss.
G2. Besitzverhältnisse im Dateisystem sind schief
- Kurzfix, falls SSH-Zugang zum Container gebraucht wird:
pct exec 201 -- chown 0:0 /etc/passwdundpct exec 201 -- chown 0:42 /etc/shadow - Langfristig: Container sauber neu aufsetzen
Warum: Das Dateisystem stammt aus einem unprivilegierten Container (alles liegt mit
UID 100000 auf der Platte), läuft aber jetzt privilegiert, also ohne UID-Mapping. root
besitzt dadurch praktisch keine Datei im System. Zeitstempel belegen es: alles vom 26.02.2026
mit UID 100000, nur /etc/hostname vom 06.09. mit 0:0 — das schreibt Proxmox von außen.
Symptom war passwd: Authentication token manipulation error. Das System läuft trotzdem,
weil root per CAP_DAC_OVERRIDE die Rechteprüfung überspringt; Werkzeuge wie passwd, die
strenger prüfen, fallen darüber. Weitere solche Merkwürdigkeiten sind zu erwarten.
⚠️ Kein chown -R über das ganze Dateisystem — nicht am laufenden Container und schon
gar nicht mit Docker darin. Postgres und die Supabase-Container haben eigene UIDs (999, 70, …),
die dabei zerschossen würden.
Kein Blocker: pct enter 201 gibt jederzeit eine Root-Shell ohne Passwort. Ein
Root-Passwort wird nur für direkten SSH-Zugang gebraucht.
G3. Supabase Studio ist nicht erreichbar
- Auf 201 prüfen:
docker compose ps— laufen die Container? - Port klären: Studio hängt hinter Kong auf 8000, nicht 8001
scripts/supabase-setup.shkorrigieren: falsche Port-Ausgabe und Shebang#!/bash
Warum: http://<IP>:8001 antwortet nicht. Die Zahl stammt aus einer echo-Zeile in
scripts/supabase-setup.sh, die nirgends eine Portfreigabe setzt — beim offiziellen
supabase/docker-Stack ist Studio nur über Kong auf 8000 erreichbar. Zusätzlich hat das
Skript #!/bash statt #!/bin/bash und ist so gar nicht direkt ausführbar.
Kleinkram (ohne Eile)
- 114
console.logaus dem Produktionscode entfernen (die 275console.errorsind in Ordnung) NEXTAUTH_SECRETauf 32 Byte verlängern (erledigt sich mit B3)- API-Routen: JSON-401 statt HTML-Redirect für nicht angemeldete Aufrufe
deploy.shunddocker-compose.ymllöschen oder als veraltet markieren — sie beschreiben ein Portainer-Setup, das nicht mehr verwendet wird- Eigener Auth-Check in
apple/syncundweather/geocode(aktuell nur durch die Middleware geschützt — zusätzliche Absicherung schadet nicht)