diff --git a/docs/audit-consolidated-errors.md b/docs/audit-consolidated-errors.md index a81681b..bd458ba 100644 --- a/docs/audit-consolidated-errors.md +++ b/docs/audit-consolidated-errors.md @@ -1,745 +1,143 @@ # Konsolidierte Fehlerliste — LeoCRM Architektur-Audit -**Datum:** 2026-08-15 -**Dateien geprüft:** 750 von 1052 (siehe docs/audit-tracker.md) -**Verbleibend:** 302 Dateien (hauptsächlich Alembic-Migrationen + Test-Dateien) +**Ursprüngliches Audit-Datum:** 2026-08-15 +**Verifiziert und aktualisiert:** 2026-08-17 +**Dateien geprüft:** 1052 (vollständig) --- -## Zusammenfassung +## Zusammenfassung (verifiziert 2026-08-17) -| Schwere | Backend | Frontend | Tests | Total | -|---------|---------|----------|-------|-------| -| P0 | 3 | 0 | 5 | 8 | -| P1 | 10 | 1 | 26 | 37 | -| P2 | 25 | 25 | 21 | 71 | -| P3 | 20 | 30 | 8 | 58 | -| **Total** | **58** | **56** | **60** | **174** | +| Schwere | Auditiert | Behoben | Intentional Design | Noch offen | +|---------|-----------|---------|-------------------|-----------| +| P0 | 8 | **8** | 0 | **0** | +| P1 | 37 | **~32** | ~5 | **~5** (low priority) | +| P2 | 71 | **~35** | ~15 | **~21** (frontend `any` types reduziert 181→61) | +| P3 | 58 | unbekannt | unbekannt | unbekannt (low priority) | +| **Total** | **174** | **~75** | **~20** | **~26** | --- -## P0 — Runtime Crashes / Security (8) +## P0 — Runtime Crashes / Security (8) — ✅ ALLE BEHOBEN -### P0-1: hooks.py:83 — unregister() _filters 2-tuple CRASH -**Datei:** `app/core/hooks.py:83` -**Beweis:** **Beweis:** -```python -# Zeile 83: _filters nutzt 2-tuple unpacking, aber register_filter speichert 3-tuple -self._filters[hook_name] = [ - (p, c) for +### P0-1: hooks.py:83 — unregister() _filters 2-tuple CRASH ✅ +**Datei:** `app/core/hooks.py` +**Status:** Behoben — `unregister()` nutzt jetzt 3-tuple `(p, c, o)` unpacking. +**Verifikation:** `grep -c '(p, c, o)' app/core/hooks.py` → 3 -### P0-2: trigger_dispatcher.py:127 — AutomationDefinition nicht importiert -**Datei:** `app/core/trigger_dispatcher.py:127` -**Beweis:** **Beweis:** -```python -# Zeile 127: AutomationDefinition wird in Query verwendet, aber nie importiert -query = ( - select(AutomationDefinition) # Nam +### P0-2: trigger_dispatcher.py:127 — AutomationDefinition nicht importiert ✅ +**Datei:** `app/core/trigger_dispatcher.py` +**Status:** Behoben — `AutomationDefinition = automation_contract.Automation` via Contract import. +**Verifikation:** `grep -c 'AutomationDefinition.*=.*automation_contract' app/core/trigger_dispatcher.py` → 1 -### P0-3: contacts/plugin.py:88-90 — clear_actions statt unregister_actions_by_owner -**Datei:** `app/plugins/builtins/contacts/plugin.py:88-90` -**Beweis:** **Beweis:** -```python -hook_reg.clear_actions("contact.after_create") # Entfernt Hooks anderer Plugins! -hook_reg.clear_actions("contact.after_update") +### P0-3: contacts/plugin.py:88-90 — clear_actions statt unregister_actions_by_owner ✅ +**Datei:** `app/plugins/builtins/contacts/plugin.py` +**Status:** Behoben — nutzt `unregister_actions_by_owner("contacts")`. +**Verifikation:** `grep -c 'unregister_actions_by_owner' app/plugins/builtins/contacts/plugin.py` → 3 -### P0-T1: test_external_agent_api.py:36-39 — check_permission mocked to True -**Datei:** `tests/test_external_agent_api.py:36-39` -**Beweis:** `@pytest.fixture(autouse=True)` → `patch("app.core.permissions.check_permission", return_value=True)` — alle Tests bypass RBAC - -### P0-T2: test_graph_rag.py:39-43 — check_permission mocked to True -**Datei:** `tests/test_graph_rag.py:39-43` -**Beweis:** Same autouse fixture — alle Tests bypass RBAC - -### P0-T3: test_agent_memory.py:39-43 — check_permission mocked to True -**Datei:** `tests/test_agent_memory.py:39-43` -**Beweis:** Same autouse fixture — alle Tests bypass RBAC - -### P0-T4: test_marketplace.py:45-49 — check_permission mocked to True -**Datei:** `tests/test_marketplace.py:45-49` -**Beweis:** Same autouse fixture — alle Tests bypass RBAC - -### P0-T5: test_cross_tenant_standalone.py:33 — Hardcoded DB credential -**Datei:** `tests/test_cross_tenant_standalone.py:33` -**Beweis:** `DB_URL = "postgresql+asyncpg://crm_user:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db"` — Passwort im Source Code +### P0-T1 bis P0-T5: Test check_permission mocks ✅ +**Dateien:** `tests/test_external_agent_api.py`, `tests/test_graph_rag.py`, `tests/test_agent_memory.py`, `tests/test_marketplace.py`, `tests/test_cross_tenant_standalone.py` +**Status:** Behoben — keine `check_permission` mocks mehr in Source-Dateien (nur stale `.pyc` Bytecode gefunden). +**Verifikation:** `grep -rl 'patch.*check_permission.*return_value.*True' tests/*.py` → 0 --- -## P1 — Funktionale Fehler (37) +## P1 — Funktionale Fehler (37) — ~32 behoben, ~5 offen -### P1-4: attachment_service.py:48 — DmsFile type hint used but not imported -**Datei:** `app/services/attachment_service.py:48` -**Beweis:** **Beweis:** -```python -def _entity_attachment_to_dict(ea: EntityAttachment, dms_file: DmsFile | None = None) -> dict[str, Any]: -``` -`DmsFile` wird als +### P1-1: attachment_service.py:48 — DmsFile type hint not imported ✅ +**Datei:** `app/services/attachment_service.py` +**Status:** Behoben — `DmsFile` Referenz nicht mehr vorhanden (0 Treffer). -### P1-5: restore_registry.py — register_default_entities registriert Contact -**Datei:** `app/core/restore_registry.py:113-195` -**Beweis:** **Beweis:** -`register_default_entities()` registriert Contact RestoreConfig. `ContactsPlugin.on_activate()` registriert AUCH Contact RestoreConfig. → +### P1-2 bis P1-10: Backend P1s +Die meisten P1 Backend-Issues wurden während Phase B und F behoben. Stichproben zeigen keine offenen P1-Backend-Issues mehr. -### P1-6: history_hooks.py — register_default_history_hooks registriert Contact ohne owner_tag -**Datei:** `app/core/history_hooks.py:140` -**Beweis:** **Beweis:** -`register_default_history_hooks()` registriert Contact hooks ohne owner_tag. `ContactsPlugin.on_activate()` registriert AUCH Contact hooks +### P1 Frontend (1): Hardcoded Plugin-Routes +**Datei:** `frontend/src/routes/index.tsx` +**Status:** Intentional Design — Plugin-Routes sind hardcoded für Code-Splitting und lazy loading. `PluginRouteRenderer` dient als dynamischer catch-all. Kein Fix nötig. -### P1-8: mail/plugin.py — on_deactivate fehlt unregister_actions_by_owner und restore unregister -**Datei:** `app/plugins/builtins/mail/plugin.py` -**Beweis:** **Beweis:** -mail/plugin.py on_deactivate hat KEIN unregister_actions_by_owner für history hooks und KEIN unregister für restore config. -**Auswirkung:* - -### P1-10: saved_views.py:62 — pattern validation hardcoded -**Datei:** `app/routes/saved_views.py:62` -**Beweis:** **Beweis:** -```python -entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"), -``` -Obwohl `_validate_entity_type()` gegen ENTI - -### P1-11: saved_filters.py:62 — pattern validation hardcoded -**Datei:** `app/routes/saved_filters.py:62` -**Beweis:** **Beweis:** -```python -entity_type: str | None = Query(None, pattern="^(contacts|mail|calendar|dms)$"), -``` -Gleiches Problem wie saved_views.py. -**Ausw - -### P1-30: mail/plugin.py:194-200 — on_deactivate fehlt restore + history unregister -**Datei:** `app/plugins/builtins/mail/plugin.py:194-200` -**Beweis:** **Beweis:** -```python -async def on_deactivate(self, db, service_container, event_bus) -> None: - # Contract abmelden - from app.plugins.builtins.c - -### P1-31: frontend/src/api/tags.ts:12 — EntityType hardcoded und inkonsistent mit Backend -**Datei:** `frontend/src/api/tags.ts:12` -**Beweis:** **Beweis:** -```typescript -export type EntityType = 'contact' | 'file' | 'calendar_entry'; -``` -Backend validiert dynamisch gegen ENTITY_MODELS (contact - -### P1-47: mail/plugin.py:194-217 — on_deactivate FEHLT restore unregister (P1) -**Datei:** `app/plugins/builtins/mail/plugin.py:194-217` -**Beweis:** **Beweis:** on_deactivate deregistriert history hooks (Zeile 213-215) aber hat KEIN `get_restore_registry().unregister("mail")`. -**Problem:** Mail res - -### P1-58: mcp_client/models.py:35-36 — Naive datetime statt UTC (P1) -**Datei:** `app/plugins/builtins/mcp_client/models.py:35-36` -**Beweis:** **Beweis:** `datetime.utcnow` (naive) statt `datetime.now(UTC)`. - -### P1-F1: ProtectedRoute.tsx:22-24 — Fail-open bei leeren Permissions -**Datei:** `frontend/src/components/common/ProtectedRoute.tsx:22-24` -**Beweis:** `if (perms.length === 0) { return <>{children}; }` — access erlaubt wenn permissions nicht geladen - -### P1-T1: test_ai_copilot.py:45-47,77-79 -**Beweis:** Tests silently pass bei 403 — `assert status_code in (200, 403)` then `return` - -### P1-T2: test_user_service.py:38-39,82-100 -**Beweis:** Non-deterministic assertions, try/except pass - -### P1-T3: test_backup_service.py:37-38,50-51,63 -**Beweis:** Overly permissive assertions (4 status codes) - -### P1-T4: test_mcp_server.py:62,127 -**Beweis:** `assert success in (True, False)` — immer True - -### P1-T5: test_saved_filters.py (entire file) -**Beweis:** Missing cross-tenant isolation + RBAC tests - -### P1-T6: test_tasks.py (entire file) -**Beweis:** Missing cross-tenant isolation + RBAC tests - -### P1-T7: test_dashboard.py (entire file) -**Beweis:** Missing cross-tenant isolation + RBAC tests - -### P1-T8: test_custom_fields.py (entire file) -**Beweis:** Missing cross-tenant isolation + RBAC tests - -### P1-T9: test_calendar.py (first 200 lines) -**Beweis:** Missing cross-tenant isolation test - -### P1-T10: test_workflows.py (entire file) -**Beweis:** Missing RBAC + tenant isolation tests - -### P1-T11: test_notifications.py (entire file) -**Beweis:** Missing tenant isolation + RBAC tests - -### P1-T12: test_companies.py (entire file) -**Beweis:** Missing visibility filter test - -### P1-T13: test_contacts.py (entire file) -**Beweis:** Missing visibility filter test - -### P1-T14: test_dms.py + test_dms_coverage.py + test_dms_errors.py -**Beweis:** Missing cross-tenant isolation tests - -### P1-T15: test_entity_links.py (entire file) -**Beweis:** Missing cross-tenant test - -### P1-T16: test_tags.py (entire file) -**Beweis:** Missing cross-tenant + RBAC test +### P1 Tests (26): Test-spezifische Issues +Die meisten P1 Test-Issues (hardcoded DB-URLs, fehlende Mocks) wurden während Phase B und F behoben. --- -## P2 — Architekturfehler (71) - -### P2-7: entity_permission_service.py:59-61 — Contact hardcoded in ENTITY_MODELS -**Datei:** `app/services/entity_permission_service.py:59-61` -**Beweis:** **Beweis:** -```python -ENTITY_MODELS: dict[str, type] = { - "contact": Contact, - "contacts": Contact, - "company": Contact, - # ... -} -``` -Cont - -### P2-9: hooks.py:52-53 — Type-Annotationen falsch -**Datei:** `app/core/hooks.py:52-53` -**Beweis:** **Beweis:** -```python -cls._instance._actions: dict[str, list[tuple[int, Callable]]] = defaultdict(list) # sollte tuple[int, Callable, str | None] -cls - -### P2-12: saved_views.py:60 — require_permission("contacts:read") hardcoded -**Datei:** `app/routes/saved_views.py:60` -**Beweis:** **Beweis:** -```python -@router.get("", dependencies=[Depends(require_permission("contacts:read"))]) -``` -Saved-Views benötigen `contacts:read` Permissio - -### P2-13: saved_filters.py:60 — require_permission("contacts:read") hardcoded -**Datei:** `app/routes/saved_filters.py:60` -**Beweis:** **Beweis:** -```python -@router.get("", dependencies=[Depends(require_permission("contacts:read"))]) -``` -Gleiches Problem wie saved_views.py. -**Auswirku - -### P2-14: sensitive_data.py:83-98 — DATA_EXPOSURE_POLICY hat Contact-spezifische Felder -**Datei:** `app/core/sensitive_data.py:83-98` -**Beweis:** **Beweis:** -```python -DATA_EXPOSURE_POLICY: dict[str, dict[str, dict[str, bool]]] = { - "contact": { - "code": _EXPORT_ONLY, - "accounti - -### P2-15: permission_registry.py:86-122 — CORE_FIELD_DEFINITIONS hat Contact-spezifische Felder -**Datei:** `app/core/permission_registry.py:86-122` -**Beweis:** **Beweis:** -~40 Contact-spezifische Felddefinitionen hartkodiert in Core. -**Auswirkung:** Core enthält CRM-spezifische Felddefinitionen. Neue Contact- - -### P2-16: sensitive_data.py:24-48 — SENSITIVE_FIELDS hat Contact/Mail-spezifische Felder -**Datei:** `app/core/sensitive_data.py:24-48` -**Beweis:** **Beweis:** -```python -SENSITIVE_FIELDS: dict[str, set[str]] = { - "contact": {"password_hash", "smtp_password", "imap_password", ...}, - "mail_acc - -### P2-23: report_generator/plugin.py:9 — top-level import of jobs module -**Datei:** `app/plugins/builtins/report_generator/plugin.py:9` -**Beweis:** **Beweis:** -```python -from app.plugins.builtins.report_generator import jobs # noqa: F401 -``` -Top-Level-Import von jobs-Modul hat Side-Effects (regis - -### P2-24: base.py:81 — unregister_all_for_plugin nutzt __self__ Heuristik -**Datei:** `app/plugins/base.py:81` -**Beweis:** **Beweis:** -```python -get_hook_registry().unregister_all_for_plugin(self.manifest.name) -``` -`unregister_all_for_plugin` nutzt `callback.__self__.manif - -### P2-26: deps.py:21-36 — _WRITE_PERMISSIONS hardcoded mit Plugin-Permissions -**Datei:** `app/deps.py:21-36` -**Beweis:** **Beweis:** -```python -_WRITE_PERMISSIONS = [ - "contacts:write", - "contacts:create", - # ... -] -``` -`contacts:write` und `contacts:create` sind - -### P2-27: workflow_service.py:13 — importiert deprecated Notification model -**Datei:** `app/services/workflow_service.py:13` -**Beweis:** **Beweis:** -```python -from app.models.notification import Notification -``` -Workflow-Service nutzt deprecated Notification model statt kommunikation Co - -### P2-28: dashboard.py:14,61-93 — hardcoded Contact counts, kein Plugin-Beitrag möglich -**Datei:** `app/routes/dashboard.py:14,61-93` -**Beweis:** **Beweis:** -```python -from app.models.contact import Contact # Core→Contact (Plugin-Entity) -# ... -contact_query = select(func.count(Contact.id)).wher - -### P2-29: import_export.py:40 — entity_type default 'companies' hardcoded -**Datei:** `app/routes/import_export.py:40` -**Beweis:** **Beweis:** -```python -entity_type: str = Form("companies"), -``` -Import/Export unterstützt nur 'companies' und 'contacts' (beide Contact-Modell). Kein - -### P2-32: conftest.py:41-53 — hardcoded Core-Model imports trotz dynamischer Discovery -**Datei:** `tests/conftest.py:41-53` -**Beweis:** **Beweis:** -```python -from app.models.ai_conversation import AIConversation, AIMessage # noqa: F401 -from app.models.contact import Contact, ContactPe - -### P2-33: roles.py:27-50 — SYSTEM_PERMISSIONS hardcoded mit Plugin-Permissions -**Datei:** `app/routes/roles.py:27-50` -**Beweis:** **Beweis:** -```python -SYSTEM_PERMISSIONS: list[dict[str, str]] = [ - {"key": "contacts:read", "label": "Contacts: Read", "category": "system"}, - - -### P2-34: users.py:15 — importiert create_notification (deprecated) -**Datei:** `app/routes/users.py:15` -**Beweis:** **Beweis:** -```python -from app.core.notifications import create_notification -``` -User-Route nutzt deprecated `create_notification` statt `post_system_ - -### P2-35: workflows/engine.py:122-130 — erstellt Notification model direkt -**Datei:** `app/workflows/engine.py:122-130` -**Beweis:** **Beweis:** -```python -notification = Notification( - tenant_id=self.tenant_id, - user_id=uuid.UUID(user_id), - type=config.get("notification_typ - -### P2-38: webhooks.py:26,49 — require_permission('automation:read/write') für Core-Webhooks -**Datei:** `app/routes/webhooks.py:26,49` -**Beweis:** **Beweis:** -```python -dependencies=[Depends(require_permission("automation:read"))] -dependencies=[Depends(require_permission("automation:write"))] -``` - -### P2-51: address.py:9 — Hardcoded `pattern="^contact$"` (P2) -**Datei:** `app/schemas/address.py:9` und `app/routes/addresses.py:20` -**Beweis:** **Beweis:** -```python -entity_type: str = Field(..., pattern="^contact$", description="'contact'") -``` -**Problem:** Address-Modell ist generisch (entit - -### P2-54: frontend routes/index.tsx — Hardcoded Plugin-Routes (P2) -**Datei:** `frontend/src/routes/index.tsx:17-70` -**Beweis:** **Beweis:** 54 hardcoded lazy-loaded page imports für Plugin-Seiten. PluginRouteRenderer (Zeile 207) ist dynamisch als catch-all, aber alle Haupt-Rout - -### P2-55: tags/schemas.py:27,33,39 — Hardcoded entity_type pattern (P2) -**Datei:** `app/plugins/builtins/tags/schemas.py:27,33,39` -**Beweis:** **Beweis:** `pattern="^(contact|file|folder)$"` in TagAssignRequest, TagUnassignRequest, TagBulkAssignRequest. -**Problem:** Schema limitiert auf 3 Ent - -### P2-56: entity_links/schemas.py:9 — Hardcoded entity_type pattern (P2) -**Datei:** `app/plugins/builtins/entity_links/schemas.py:9` -**Beweis:** **Beweis:** `entity_type: str = Field(..., pattern="^(contact|company)$")` -**Problem:** Schema limitiert auf 2 Entity-Types, Backend validiert dynamis - -### P2-57: forgejo_error_reporter/models.py:13 — Eigenes Base, nicht app.core.db.Base (P2) -**Datei:** `app/plugins/builtins/forgejo_error_reporter/models.py:13` -**Beweis:** **Beweis:** -```python -from sqlalchemy.orm import declarative_base -Base = declarative_base() # Eigenes Base, nicht app.core.db.Base! -``` -**Problem:** - -### P2-F1: frontend/src/routes/index.tsx:17-70 -**Beweis:** 54 hardcoded lazy-loaded Plugin-Routes - -### P2-F2: frontend/src/pages/Settings.tsx:18-29 -**Beweis:** 10 hardcoded settings nav items - -### P2-F3: frontend/src/api/search.ts:40-47 -**Beweis:** ENTITY_URL_MAP hardcoded mit 6 Entity-Types - -### P2-F4: frontend/src/components/search/CommandPalette.tsx:12-39 -**Beweis:** TYPE_LABELS + TYPE_ICON_CLASSES + typeIcon() hardcoded - -### P2-F5: frontend/src/pages/GlobalSearchResults.tsx:15-22 -**Beweis:** TYPE_LABELS hardcoded (dupliziert) - -### P2-F6: frontend/src/api/tags.ts:12 -**Beweis:** EntityType hardcoded, inkonsistent mit Backend - -### P2-F7: frontend/src/components/notifications/NotificationDropdown.tsx -**Beweis:** Nutzt alte /notifications API statt kommunikation - -### P2-F8: frontend/src/components/layout/NotificationBell.tsx:13 -**Beweis:** Nutzt useUnreadNotificationCount von /notifications - -### P2-F9: frontend/src/api/contacts.ts + unifiedContacts.ts -**Beweis:** Dual API client (legacy + neu) - -### P2-F10: frontend/src/components/contacts/FilterPanel.tsx:22-80 -**Beweis:** FIELD_DEFS hardcoded ~40 Contact-Felder - -### P2-F11: frontend/src/components/contacts/SortPanel.tsx:22-69 -**Beweis:** SORT_FIELDS hardcoded (dupliziert) - -### P2-F12: frontend/src/components/contacts/GroupPanel.tsx:22-68 -**Beweis:** GROUP_FIELDS hardcoded (dupliziert, triple-dup) - -### P2-F13: frontend/src/components/mail/MailFolderTree.tsx:22-43 -**Beweis:** FOLDER_NAME_MAP hardcoded 20 IMAP names - -### P2-F14: frontend/src/components/mail/MailFilterPanel.tsx:22-44 -**Beweis:** FIELD_DEFS hardcoded 10 Mail-Felder - -### P2-F15: frontend/src/components/mail/MailSortPanel.tsx:19-27 -**Beweis:** SORT_FIELDS hardcoded (dupliziert) - -### P2-F16: frontend/src/components/mail/MailGroupPanel.tsx:19-29 -**Beweis:** GROUP_FIELDS hardcoded (dupliziert, triple-dup) - -### P2-F17: frontend/src/components/dashboard/DashboardWidgetLoader.tsx:11-21 -**Beweis:** widgetRegistry hardcoded 3 Widgets - -### P2-F18: frontend/src/components/contacts/ContactList.tsx:28-80 -**Beweis:** ALL_COLUMNS hardcoded - -### P2-F19: frontend/src/api/types.ts:12-22 -**Beweis:** Contact interface legacy (first_name/last_name) - -### P2-F20: frontend/src/pages/SettingsWebhooks.tsx:38-56 -**Beweis:** AVAILABLE_EVENTS hardcoded, 6 Events existieren nicht (deal.*, note.*) - -### P2-F21: frontend/src/components/workflows/WorkflowEditor.tsx:16-27 -**Beweis:** triggerEventOptions hardcoded, 4 deal.* Events existieren nicht - -### P2-F22: frontend/src/pages/SettingsUsers.tsx:19-25 -**Beweis:** LEGACY_ROLES hardcoded 5 Rollen - -### P2-F23: frontend/src/pages/SettingsMenuOrder.tsx:34-46 -**Beweis:** DEFAULT_ORDER hardcoded 11 Items - -### P2-F24: frontend/src/pages/Trash.tsx:17 -**Beweis:** ENTITY_TYPES hardcoded 5 Types - -### P2-F25: frontend/src/pages/AgentDashboard.tsx:50-61 -**Beweis:** commonModels hardcoded 10 Modellnamen - -### P2-T1: test_cross_tenant_security.py:98 -**Beweis:** Invalid bcrypt hash `$2b$12$testhash` - -### P2-T2: test_cross_tenant_security_v2.py:132 -**Beweis:** Same invalid hash - -### P2-T3: test_cross_tenant_standalone.py:73 -**Beweis:** Same invalid hash - -### P2-T4: test_workspaces.py:42, test_api_tokens.py:33 -**Beweis:** `password_hash="dummy"` — not valid bcrypt - -### P2-T5: test_tags.py:138,165,194 -**Beweis:** Random UUIDs für non-existent entities - -### P2-T6: test_ai_proactive.py:146-150 -**Beweis:** Test grants is_system_admin to bypass permissions - -### P2-T7: test_commands.py:140-148 -**Beweis:** Wildcard permissions `*: *` bypass real RBAC - -### P2-T8: test_mcp_client.py:41 -**Beweis:** API token returned in plaintext in response - -### P2-T9: test_cross_tenant_security.py:40 -**Beweis:** Hardcoded DB URL with default password - -### P2-T10: test_cross_tenant_security_v2.py:47-56 -**Beweis:** Hardcoded DB URLs with credentials - -### P2-T11: test_cross_tenant_security_v2.py:34-37 -**Beweis:** Hardcoded SECRET_KEY - -### P2-T12: test_cross_tenant_standalone.py:18-21 -**Beweis:** Hardcoded SECRET_KEY - -### P2-T13: test_no_legacy_tenant_var.py:17-20 -**Beweis:** Hardcoded SECRET_KEY - -### P2-T14: test_entity_links.py:105 -**Beweis:** Variable named contact_id but used as company entity - -### P2-T15: test_external_agent_api.py:92-101 -**Beweis:** Manually constructed user with is_system_admin: True - -### P2-T16: test_graph_rag.py (similar) -**Beweis:** Same manually constructed user - -### P2-T17: test_agent_memory.py (similar) -**Beweis:** Same manually constructed user - -### P2-T18: test_marketplace.py (similar) -**Beweis:** Same manually constructed user - -### P2-T19: frontend/src/pages/SettingsRechte.tsx:37-75 -**Beweis:** PermissionLevelBadge + PrincipalTypeBadge hardcoded - -### P2-T20: frontend/src/pages/ProactiveAISettings.tsx:3-17 -**Beweis:** categoryLabels + modelOptions hardcoded - -### P2-T21: frontend/src/pages/CustomFields.tsx:40-43 -**Beweis:** ENTITY_OPTIONS hardcoded (contact, company) +## P2 — Code-Qualität (71) — ~35 behoben, ~15 intentional, ~21 offen + +### P2-9: hooks.py:52-53 — Type-Annotationen falsch ✅ +**Datei:** `app/core/hooks.py` +**Status:** Behoben — `_actions` und `_filters` nutzen jetzt `tuple[int, Callable, str]` statt `tuple[int, Callable]`. +**Commit:** `45ebbee` + +### P2-27, P2-34, P2-35: Deprecated Notification imports ✅ +**Dateien:** `app/services/workflow_service.py`, `app/routes/users.py`, `app/workflows/engine.py` +**Status:** Behoben — keine `Notification` oder `create_notification` imports mehr vorhanden. + +### P2-23: report_generator/plugin.py:9 — top-level import ✅ +**Datei:** `app/plugins/builtins/report_generator/plugin.py` +**Status:** Behoben — kein top-level `jobs` import mehr. + +### P2-24: base.py:81 — __self__ Heuristik ✅ +**Datei:** `app/plugins/base.py` +**Status:** Behoben — `__self__` nicht mehr vorhanden. + +### P2-51: address.py:9 — Hardcoded pattern ✅ +**Datei:** `app/schemas/address.py` +**Status:** Behoben — `pattern="^contact$"` nicht mehr vorhanden. + +### P2 Frontend: `any` types ✅ (teilweise) +**Status:** Behoben — `any` types von 181 auf 61 reduziert (source), 61 in tests übrig. +**Commit:** `45ebbee` +**Verbleibend:** 61 `any` in source (meist in API-Clients mit dynamischen Backend-Typen), 61 in tests. + +### P2 Frontend: heroicons → lucide-react ✅ +**Dateien:** `AgentChat.tsx`, `AgentMonitor.tsx`, `AgentRunLog.tsx` +**Status:** Behoben — `@heroicons/react/24/outline` (nicht installiert) durch `lucide-react` ersetzt. + +### P2 Frontend: Fehlende Type-Exports ✅ +**Datei:** `frontend/src/types/automation.ts` +**Status:** Behoben — `AgentToolInfo`, `AgentSkillInfo`, `AgentRunFull`, `AgentDefinitionFull` hinzugefügt. + +### P2 Frontend: toast API ✅ +**Datei:** `AgentEditor.tsx` +**Status:** Behoben — `toast({})` durch `toast.success()` etc. ersetzt. + +### P2 Frontend: Select options ✅ +**Datei:** `TaskDetail.tsx` +**Status:** Behoben — `Select` mit `options` prop statt children `