# 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) --- ## Zusammenfassung | 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** | --- ## P0 — Runtime Crashes / Security (8) ### 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-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-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-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 --- ## P1 — Funktionale Fehler (37) ### 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-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-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-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 --- ## 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) --- ## P3 — Dead Code / Code Quality / Deprecated (58) ### P3-19: registry.py:36 — _mounted_routes ist Dead Code **Datei:** `app/plugins/registry.py:36` **Beweis:** **Beweis:** `self._mounted_routes: dict[str, list[Any]] = {}` wird initialisiert aber nie befüllt. Route-Removal-Logik wurde entfernt (Gate-Modell dok ### P3-20: ai_assistant/plugin.py:96-98 — direkter Import von kommunikation.contracts **Datei:** `app/plugins/builtins/ai_assistant/plugin.py:96-98` **Beweis:** **Beweis:** ```python from app.plugins.builtins.kommunikation.contracts import ( get_participant_registry, ) ``` **Bewertung:** Deklariert in `dep ### P3-21: system_notif/plugin.py:161 — direkter Import von kommunikation.contracts **Datei:** `app/plugins/builtins/system_notif/plugin.py:161` **Beweis:** **Beweis:** ```python from app.plugins.builtins.kommunikation.contracts import create_plugin_room, send_message ``` **Bewertung:** Deklariert in `depe ### P3-22: graph_rag/plugin.py:44,57 — direkter Import von unified_search.contracts **Datei:** `app/plugins/builtins/graph_rag/plugin.py:44,57` **Beweis:** **Beweis:** ```python from app.plugins.builtins.unified_search.contracts import get_search_registry ``` **Bewertung:** Deklariert in `dependencies=["u ### P3-25: entity_permission_service.py:30 — importiert create_notification **Datei:** `app/services/entity_permission_service.py:30` **Beweis:** **Beweis:** ```python from app.core.notifications import create_notification ``` `create_notification` ist deprecated und nutzt jetzt `get_contract("k ### P3-36: schemas/common.py:22-58 — Notification schemas für deprecated system **Datei:** `app/schemas/common.py:22-58` **Beweis:** **Beweis:** ```python class NotificationResponse(BaseModel): ... class NotificationListResponse(BaseModel): ... class UnreadCountResponse(BaseModel): ### P3-37: custom_fields.py:42 — greift auf registry._plugins direkt zu **Datei:** `app/routes/custom_fields.py:42` **Beweis:** **Beweis:** ```python for plugin in registry._plugins.values(): ``` Greift auf privates Attribut `_plugins` zu statt öffentlichen API (`list_discovere ### P3-48: entity_attachment.py:45-49 — __import__ inline statt func import (P3) **Datei:** `app/models/entity_attachment.py:45-49` **Beweis:** **Beweis:** ```python created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=__import__('sqlalchemy ### P3-49: ai_copilot_service.py:18-19 — Duplicate Contact import (P3) **Datei:** `app/services/ai_copilot_service.py:18-19` **Beweis:** **Beweis:** ```python from app.models.contact import Contact from app.models.contact import Contact # Duplicate! ``` ### P3-50: system_settings.py:44-54 — Duplicate field definitions (P3) **Datei:** `app/schemas/system_settings.py:44-54` **Beweis:** **Beweis:** ```python class SystemSettingsResponse(BaseModel): # Zeilen 45-48: tax_number: str | None = None vat_id: str | None = None ### P3-52: unified_search hardcoded entity maps (P2) **Dateien:** `app/plugins/builtins/unified_search/search_engine.py:19-24`, `lifecycle.py:22-27`, `jobs.py:16-21` **Beweis:** Drei separate hardcoded Entity-Maps für dieselben 4 Entity-Types (contact, mail, file, event). Duplikation, nicht erweiterbar. **Schwere:** P2 — Architekturfehler, dreifache Duplikation. ### FEHLER 53: ai_proactive/services.py:26-27 — Duplicate Contact import (P3) **Datei:** `app/plugins/builtins/ai_proactive/services.py:26-27` **Beweis:** **Beweis:** ```python from app.models.contact import Contact, ContactPerson from app.models.contact import Contact, ContactPerson # Duplicate! ``` ### P3-F1: frontend/src/pages/Settings.tsx:5 **Beweis:** `import * as LucideIcons` lädt ALLE Icons ### P3-F2: frontend/src/pages/Settings.tsx:38 **Beweis:** `(LucideIcons as any)[p.icon]` — any type cast ### P3-F3: frontend/src/components/contacts/ContactDetail.tsx:11 **Beweis:** `import * as LucideIcons` lädt ALLE Icons ### P3-F4: frontend/src/components/contacts/ContactDetail.tsx:67-74 **Beweis:** 8x `(contact as any)` type casts ### P3-F5: frontend/src/components/ai/ChatWindow.tsx:39-41 **Beweis:** inline styles für animationDelay ### P3-F6: frontend/src/components/mail/MailList.tsx:62 **Beweis:** inline style paddingLeft ### P3-F7: frontend/src/components/mail/MailDetail.tsx:49-52 **Beweis:** iframe HTML rendering (potential XSS) ### P3-F8: frontend/src/components/contacts/CustomFieldRenderer.tsx:15-16 **Beweis:** `any` types ### P3-F9: frontend/src/components/SavedFilters.tsx:22,47,56 **Beweis:** `any` types ### P3-F10: frontend/src/store/windowStore.ts:8-9 **Beweis:** `ComponentType` und `Record` ### P3-F11: frontend/src/store/commStore.ts:27,61 **Beweis:** `Record` und `reactions: any[]` ### P3-F12: frontend/src/hooks/useCommWebSocket.ts:31 **Beweis:** `console.log` statt strukturiertem Logger ### P3-F13: frontend/src/hooks/useAIContext.ts:5 **Beweis:** `entityData?: any` ### P3-F14: frontend/src/api/auth.ts:28,36,60 **Beweis:** `any` type casts ### P3-F15: frontend/src/pages/Login.tsx:41 **Beweis:** `error: any` ### P3-F16: frontend/src/pages/Workflows.tsx:59,70 **Beweis:** `err: any` ### P3-F17: frontend/src/pages/AuditLog.tsx:37 **Beweis:** `ColumnDef` ### P3-F18: frontend/src/components/comm/blocks/BlockRenderer.tsx:59 **Beweis:** hardcoded German string ### P3-F19: frontend/src/components/comm/blocks/MiniAppBlock.tsx:13,24 **Beweis:** hardcoded German strings ### P3-F20: frontend/src/components/comm/blocks/ActionCardBlock.tsx:59 **Beweis:** hardcoded German string ### P3-F21: frontend/src/components/comm/blocks/HtmlBlock.tsx:17-20 **Beweis:** redundante regex vor DOMPurify ### P3-F22: frontend/src/components/shared/CsvImportDialog.tsx:48,71,78 **Beweis:** hardcoded German strings ### P3-F23: frontend/src/components/shared/DataGrid.tsx:20,37 **Beweis:** `any` types ### P3-F24: frontend/src/components/dms/FileDetails.tsx:73 **Beweis:** inline style DOM-Manipulation ### P3-F25: frontend/src/pages/NoAccessPage.tsx:9-13 **Beweis:** hardcoded German strings ohne i18n ### P3-F26: frontend/src/pages/ActivityTimeline.tsx:25-26 **Beweis:** hardcoded German strings ### P3-F27: frontend/src/pages/SettingsBackup.tsx:38 **Beweis:** hardcoded de-DE locale ### P3-F28: frontend/src/pages/PasswordResetConfirm.tsx:15 **Beweis:** hardcoded English validation message ### P3-F29: frontend/src/pages/DmsTrash.tsx:27-34 **Beweis:** leere Implementation (Trash-Endpoint fehlt) ### P3-F30: frontend/src/pages/GuestLogin.tsx:16 + GuestContacts.tsx:16 **Beweis:** hardcoded German strings ### P3-T1: test_agent_subtasks.py:29-31,35-37 **Beweis:** Duplicate fixture definition ### P3-T2: test_cross_tenant_security.py:187-188 **Beweis:** Duplicate @pytest.mark.asyncio ### P3-T3: test_tenant.py:189 **Beweis:** Potential KeyError (custom_role) ### P3-T4: test_api_documentation.py:10 **Beweis:** sys.path.insert path manipulation ### P3-T5: test_backup_restore.py:15 **Beweis:** sys.path.insert path manipulation ### P3-T6: test_ai_deploy.py:13 **Beweis:** sys.path.insert path manipulation ### P3-T7: test_ai_health_check.py:13 **Beweis:** sys.path.insert path manipulation ### P3-T8: test_dms_coverage.py:13 **Beweis:** Unused import --- ## Root Causes (4) 1. **Keine definierte Core/Plugin-Grenze** — Core importiert Plugin-Modelle, -Permissions, -Felder direkt 2. **Discovery existiert, wird aber umgangen** — Schema-Patterns, roles.py SYSTEM_PERMISSIONS, frontend routes 3. **Generische Services haben kein Registrierungs-Interface** — ENTITY_MODELS, restore, history, saved_views, tags, dashboard, dedup, import/export 4. **Contract-System wird umgangen** — 14 deprecated notifications, direkte Plugin→Plugin Imports, frontend nutzt alte API --- ## Verbleibende 302 Dateien - ~100 Alembic-Migrationen (23 stichprobenartig geprüft, alle sauber) - ~80 Frontend Test-Dateien (Vitest __tests__/) - ~55 Backend Test-Dateien (vom Test-Audit-Subordinate abgedeckt — 60 Fehler gefunden) - ~30 Frontend API-Clients und Hooks (bereits geprüft) - ~20 Frontend Stores und Utils (bereits geprüft) - ~10 Scripts (8 geprüft) - ~7 Frontend E2E Tests Siehe `docs/audit-tracker.md` für die vollständige Liste der geprüften und verbleibenden Dateien.