196 lines
9.1 KiB
Markdown
196 lines
9.1 KiB
Markdown
# LeoCRM Security Fix Plan
|
|
|
|
## Phase 1 — Kritische Sicherheitslücken (~8h)
|
|
|
|
### 1.1 ✅ 59 Permissions im Registry ergänzen
|
|
- 98 Permissions in Routes verwendet, nur 39 in CORE_PERMISSIONS
|
|
- Fehlend: ai:read, ai:write, automation:admin, mcp:read, mcp:write, calendar:read, dms:read, mail:read, tasks:read, tags:read, reports:read, search:read, agents:read, permissions:delegations:read, etc.
|
|
- Status: Implementiert und committed
|
|
|
|
### 1.2 ✅ Grants einschränken (Migration 0100)
|
|
- crm_api und crm_worker haben DELETE auf 12 sensitiven Tabellen: api_tokens, audit_log, notification_types, password_reset_tokens, plugin_allowlist, plugin_migrations, plugins, sessions, tenant_plugin_activation, tenants, user_tenants, users
|
|
- Fix: DELETE für crm_api und crm_worker auf diesen Tabellen entfernen
|
|
- crm_auth behält DELETE auf sessions + password_reset_tokens (für Logout/Reset)
|
|
- Status: Migration erstellt und committed
|
|
|
|
### 1.3 ❌ RLS auf Tabellen — ENTFERNT
|
|
- RLS auf sessions, password_reset_tokens, api_tokens, sequences, tenant_plugin_activation, user_tenants
|
|
- PROBLEM: Diese Tabellen werden vor/ohne Tenant-Context abgefragt → RLS blockiert Login/App-Startup
|
|
- WICHTIG: Kein RLS auf Tabellen die den Login blockieren!
|
|
- Status: Komplett aus Migration entfernt. Tabellen haben kein RLS wie vor der Änderung
|
|
|
|
### 1.4 ✅ Mass-Assignment Schutz
|
|
- UserCreate.role war setzbar (default viewer aber Client konnte admin senden)
|
|
- UserUpdate.role war setzbar
|
|
- Fix: UserCreate role=admin nur für is_system_admin. UserUpdate role=admin nur für is_system_admin
|
|
- Status: Implementiert und committed
|
|
|
|
### 1.5 ✅ Entity Permission Ownership-Check (PUT)
|
|
- PUT /permissions/{type}/{id}/{pid} hatte keinen Ownership-Check
|
|
- DELETE hatte einen Check
|
|
- Fix: _check_entity_ownership Hilfsfunktion, in PUT ergänzt
|
|
- Status: Implementiert und committed
|
|
|
|
### 1.6 ✅ AttachmentResponse file_path entfernt
|
|
- file_path: str in Schema Zeile 13 exponiert internen Storage-Pfad
|
|
- Fix: Aus Schema entfernt
|
|
- Status: Implementiert und committed
|
|
|
|
### 1.7 ✅ SystemSettings sensible Felder maskiert
|
|
- tax_number, iban, bic in Response Schema für alle sichtbar
|
|
- Fix: Für non-admin User maskiert ("********")
|
|
- Status: Implementiert und committed
|
|
|
|
### 1.8 ✅ File Upload MIME-Validierung + Extensions
|
|
- Nur Extension-Blocklist (ohne .php, .py, .asp, .jsp, .svg) + client-seitiger content_type (fälschbar)
|
|
- Fix: BLOCKED_EXTENSIONS ergänzt (.php, .py, .pl, .asp, .aspx, .jsp, .svg, .htaccess, .phtml, .pht, .cgi, .cfm, .erb) + ALLOWED_MIME_PREFIXES Whitelist + MIME-Validierung in upload_file
|
|
- Status: Implementiert und committed
|
|
|
|
---
|
|
|
|
## Phase 2 — Visibility Filter & Owner ID (~12h)
|
|
|
|
### 2.1 Visibility Filter in 17 Services einbauen
|
|
- ai_assistant, ai_proactive, automation, entity_links, kommunikation, mail, mcp_client, permissions, report_generator, tags, tasks, entity_permission_service, user_service, workspace_service
|
|
- apply_visibility_filter funktioniert korrekt (tenant_id + owner_id + shared permissions + admin bypass)
|
|
|
|
### 2.2 owner_id auf 26 Modellen ergänzen
|
|
- Core (15): auth, contact_folder, contact_folder_permission, contact_merge, currency, entity_policy, group, guest_user, outbox, plugin, system_settings, tax, user, user_preference, workspace
|
|
- Plugins (11): mcp_client, automation, unified_search, report_generator, entity_links, kommunikation, ai_proactive, ai_assistant, tags, permissions
|
|
|
|
### 2.3 EntityPermission Registry korrigieren
|
|
- notification, contact_folder entfernen (kein owner_id)
|
|
- entity_attachment, entity_history, subtask, calendar, folder hinzufügen
|
|
|
|
---
|
|
|
|
## Phase 3 — Weitere Sicherheitslücken (~4h)
|
|
|
|
### 3.1 WebSocket CSRF implementieren
|
|
- Kommentar sagt "skip CSRF for now" — nicht implementiert
|
|
- Fix: CSRF-Token aus Query-Parameter validieren
|
|
|
|
### 3.2 SameSite auf Lax
|
|
- session_cookie_samesite = "strict" blockiert WebSocket
|
|
- Fix: Auf "lax" ändern
|
|
|
|
### 3.3 Tenant FK CASCADE
|
|
- 3 Tabellen ohne CASCADE (contact_merge_history, tenant_plugin_activation, user_tenants)
|
|
- 10 Tabellen mit tenant_id aber ohne FK
|
|
- Fix: CASCADE ergänzen, fehlende FKs hinzufügen
|
|
|
|
---
|
|
|
|
## Phase 4 — Krisensicherheit (~9h) ✅ Abgeschlossen
|
|
|
|
### 4.1 ✅ Redis Fallback / Graceful Degradation
|
|
- Bei Redis-Ausfall funktioniert nichts mehr
|
|
- Fix: Session-Check → DB-Fallback (sessions table), Permission-Cache → DB-Fallback (direct resolve), Rate-Limiting → in-memory Fallback (InMemoryRateLimiter)
|
|
- Implementiert in: auth.py, permissions.py, rate_limit.py, middleware.py, deps.py
|
|
|
|
### 4.2 ✅ DB-Connection Retry
|
|
- Bei kurzem DB-Ausfall gibt es sofort 500er
|
|
- Fix: retry_db() mit exponentiellem Backoff (3 Versuche), 503 statt 500 bei endgültigem Ausfall
|
|
- Implementiert in: db/__init__.py (get_db), resilience.py (retry_db)
|
|
|
|
### 4.3 ✅ Circuit Breaker Middleware
|
|
- Bei wiederholten Fehlern kein automatisches Fallback
|
|
- Fix: CircuitBreaker (5 Fehler in 30s → OPEN → 503 für 60s → HALF_OPEN → Probe → CLOSED/HALF_OPEN)
|
|
- Implementiert in: resilience.py (CircuitBreaker, CircuitBreakerMiddleware), main.py (Middleware registriert)
|
|
- 30/30 Tests bestanden, produktionsverifiziert (Health 200, Login 200)
|
|
|
|
---
|
|
|
|
## Phase 5 — Architektur-Lücken (~40h) — Teilweise erledigt
|
|
|
|
### 5.1 ✅ Öffentliche Plugin-Endpoints
|
|
- Alle Plugin-Routes erforderten Auth
|
|
- Fix: PluginRouteDef.is_public field, separate Router-Mountung ohne Auth-Dependency in main.py
|
|
- permissions/public_routes.py: token-basierte Share-Link Zugriff (info, verify, download)
|
|
- Produktionsverifiziert
|
|
|
|
### 5.2 ✅ PWA aktivieren
|
|
- vite-plugin-pwa war installiert aber nicht konfiguriert
|
|
- Fix: VitePWA in vite.config.ts konfiguriert (autoUpdate, workbox, runtime caching)
|
|
- manifest.json mit Icons, theme-color, apple-mobile-web-app meta tags
|
|
- Build generiert sw.js + workbox (90 precache entries)
|
|
- Produktionsverifiziert
|
|
|
|
### 5.3 ✅ Contacts embedding + Auto-Index
|
|
- contacts hatte KEINE embedding column im ORM model
|
|
- Fix: Vector(768) embedding column zu Contact model hinzugefügt
|
|
- Migration 0002_embeddings.sql existiert bereits (HNSW index)
|
|
- ContactSearchProvider bereits implementiert (FTS + vector search)
|
|
|
|
### 5.4 ✅ Search Engine: alle Tabellen abdecken
|
|
- Nur 5 Tabellen in Suche (contacts, mails, files, calendar_entries, companies)
|
|
- Fix: 5 neue Search Provider: task, contactperson, tag, conversation, user
|
|
- Total: 10 Search Provider (war 5)
|
|
- Alle mit FTS search, tag auch mit vector search (384-dim)
|
|
- provider_registry.py aktualisiert
|
|
|
|
### 5.5 ✅ Plugin-Marketplace
|
|
- Grundlage da (discover_external, plugin_allowlist, install from ZIP, signature.py)
|
|
- Neu: marketplace/ Plugin mit MarketplaceListing model (global, keine tenant_id)
|
|
- Endpoints: list, detail, install, verify, categories
|
|
- Ed25519 Signatur-Verifikation via PluginSignature
|
|
- Config: MARKETPLACE_SERVER_URL setting
|
|
- Install-Flow: download → verify → install → activate
|
|
|
|
### 5.6 ✅ Agent Memory (persistent)
|
|
- Neu: agent_memory/ Plugin mit AgentMemory model (embedding vector(768), HNSW index)
|
|
- store_memory() mit auto-embedding
|
|
- retrieve_relevant_memories() mit pgvector cosine similarity
|
|
- Endpoints: create, list, search (semantisch), update, delete
|
|
|
|
### 5.7 ✅ GraphRAG als Provider
|
|
- Neu: graph_rag/ Plugin mit EntityRelationship model
|
|
- BFS Graph-Traversal (bidirektional, konfigurierbare Tiefe)
|
|
- GraphRAGSearchProvider im unified_search registriert
|
|
- Endpoints: create, list, traverse, delete
|
|
|
|
### 5.8 ✅ Subagents / Multi-Agent
|
|
- AgentCoordinator Klasse (create_subtask, wait_for_subtask, aggregate, cancel)
|
|
- AgentSubtask model + migration 0002_agent_subtasks.sql
|
|
- 6 neue API Endpoints für Subtask-Management
|
|
- Tools in AI tool registry registriert
|
|
|
|
### 5.9 ✅ Agent von außen erreichbar
|
|
- external_api.py: POST /run, GET /status, POST /stream (SSE)
|
|
- Bearer API Token Authentifizierung
|
|
- Rate Limiting: 10 req/min per token
|
|
- ExternalAgentRequest/Response schemas
|
|
|
|
---
|
|
|
|
## Verifizierte Fakten
|
|
|
|
| Punkt | Ergebnis |
|
|
|-------|---------|
|
|
| 59 Permissions fehlen im Registry | ✅ Bestätigt |
|
|
| crm_api + crm_worker DELETE auf 12 Tabellen | ✅ Bestätigt |
|
|
| 5 Tabellen mit tenant_id aber ohne RLS | ✅ Bestätigt |
|
|
| UserCreate.role setzbar | ✅ Bestätigt |
|
|
| UserUpdate.role setzbar | ✅ Bestätigt |
|
|
| PUT Entity Permission ohne Ownership-Check | ✅ Bestätigt |
|
|
| AttachmentResponse.file_path exponiert | ✅ Bestätigt |
|
|
| SystemSettings exponiert IBAN/BIC/Steuernummer | ✅ Bestätigt |
|
|
| SameSite = strict | ✅ Bestätigt |
|
|
| 17 Services ohne Visibility Filter | ✅ Bestätigt |
|
|
| 26 Modelle ohne owner_id | ✅ Bestätigt |
|
|
| WebSocket CSRF nicht implementiert | ✅ Bestätigt |
|
|
| File Upload ohne echte MIME-Validierung | ✅ Bestätigt |
|
|
| .env nicht in Git | ✅ Bereits gefixt |
|
|
| password_reset_tokens RLS qual=true | ❌ War falsch — Tabelle hatte kein RLS |
|
|
| DELETE Entity Permission ohne Ownership | ❌ War falsch — DELETE hat Check |
|
|
|
|
---
|
|
|
|
## WICHTIGE REGELN
|
|
|
|
- KEIN manuelles Rumgepfusche auf der Produktions-DB
|
|
- KEIN RLS auf Tabellen die den Login blockieren (sessions, password_reset_tokens, api_tokens, user_tenants, sequences, tenant_plugin_activation)
|
|
- Deploy NUR über Coolify Tool (deploy_start)
|
|
- Bei Deploy-Fehlern: Coolify DB nach Logs queryen, nicht manuell eingreifen
|
|
- Login-Logik NIEMALS ändern
|