From c50cd58d9ed9a84a63a0d81a1e4d1b97fbcc174f Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Sat, 22 Aug 2026 23:17:05 +0200 Subject: [PATCH] docs: 349 Architektur-Fehler durch Code-Review dokumentiert (Routes+Services komplett gelesen) --- docs/test-bugs.md | 792 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 792 insertions(+) diff --git a/docs/test-bugs.md b/docs/test-bugs.md index cd7bebe..25c04d7 100644 --- a/docs/test-bugs.md +++ b/docs/test-bugs.md @@ -2108,3 +2108,795 @@ Playwright E2E: 10 passed, 24 failed (BUG-011/012/013 — Mock-Daten und data-te - **Problem:** _rank() Funktion ist jetzt in 5 Dateien definiert (visibility.py, permission_resolver.py, bulk_permission_service.py, contact_folder_permission_service.py, und in permissions.py als PERM_RANK). Sollte zentralisiert werden. - **Schweregrad:** Low - **Status:** ⏳ Nicht gefixt + +### ARCH-217: auth.py — Dead Code: Response Objekt wird erstellt aber nie verwendet +- **Datei:** app/routes/auth.py:52-53 +- **Problem:** `response = Response(status_code=status.HTTP_200_OK)` wird erstellt und `response.set_cookie(...)` aufgerufen, aber dann wird ein neues `JSONResponse` Objekt erstellt (Zeile 57). Das erste Response-Objekt ist Dead Code. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-218: auth.py — logout hat ungenutzten db Parameter +- **Datei:** app/routes/auth.py:74 +- **Problem:** `db: AsyncSession = Depends(get_auth_db)` wird injiziert aber nie verwendet. Nur redis wird für logout genutzt. Verschwendet DB-Connection. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-219: auth.py — me_permissions verwendet zwei verschiedene DB-Sessions +- **Datei:** app/routes/auth.py:150 +- **Problem:** `db: AsyncSession = Depends(get_auth_db)` UND `current_user: dict = Depends(get_current_user)` — get_current_user verwendet get_db (crm_api role), get_auth_db verwendet crm_auth role. Zwei verschiedene DB-Sessions in einem Request. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-220: users.py — user.created direkt published statt über Outbox +- **Datei:** app/routes/users.py:140 +- **Problem:** `event_bus.publish('user.created', ...)` direkt statt über `enqueue_outbox_event()`. outbox.py sagt 'domain events must go through outbox for durability'. user.created ist nicht durable — geht verloren bei Crash. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-221: users.py — update_menu_order Double-Commit +- **Datei:** app/routes/users.py:350 +- **Problem:** `await db.commit()` im Route-Handler, aber `get_db()` committet bereits bei Erfolg (Zeile 248 in db/__init__.py). Double-Commit kann zu Fehlern führen. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-222: users.py — delete_user verwendet users:write statt users:delete +- **Datei:** app/routes/users.py:260 +- **Problem:** `require_permission('users:write')` für DELETE, aber CORE_PERMISSIONS hat `users:delete`. Falsche Permission — User mit write aber ohne delete können User löschen. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-223: contacts.py — pydantic Import in der Mitte der Datei +- **Datei:** app/routes/contacts.py:25 +- **Problem:** `from pydantic import BaseModel, Field` wird in der Mitte der Datei importiert (nach dem Router-Import), nicht am Anfang. Anti-Pattern — alle Imports sollten am Datei-Anfang stehen. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-224: companies.py — create_company ohne Pydantic Schema +- **Datei:** app/routes/companies.py:85 +- **Problem:** `body: dict[str, Any]` statt Pydantic Schema. Keine Validierung — beliebige Felder werden akzeptiert. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-225: companies.py — export_companies ohne visibility filter +- **Datei:** app/routes/companies.py:200 +- **Problem:** Exportiert alle Companies ohne `apply_visibility_filter()`. Jeder User mit contacts:read kann ALLE Companies exportieren, auch solche die er nicht sehen dürfte. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-226: companies.py — update_company ohne Pydantic Schema +- **Datei:** app/routes/companies.py:260 +- **Problem:** `body: dict[str, Any]` — keine Validierung. Gleicher Fehler wie ARCH-224. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-227: companies.py — get_company_emails returns empty list (Stub) +- **Datei:** app/routes/companies.py:340 +- **Problem:** `return []` — Stub/Placeholder. Endpoint existiert aber gibt immer leere Liste zurück. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-228: companies.py — created_by/updated_by Felder existieren nicht auf Contact Model +- **Datei:** app/routes/companies.py:100 +- **Problem:** `Contact(created_by=user_id, updated_by=user_id)` — aber Contact Model hat `owner_id` von OwnedMixin, nicht `created_by`/`updated_by`. Diese Felder werden still ignoriert oder verursachen Fehler. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-229: companies.py — Hooks direkt published statt über Outbox +- **Datei:** app/routes/companies.py:100,115 +- **Problem:** `do_action('company.before_create', ...)` und `do_action('company.after_create', ...)` — domain events direkt am Event Bus statt über Outbox. Nicht durable. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-230: roles.py — Core→Plugin Import (get_registry) +- **Datei:** app/routes/roles.py:22 +- **Problem:** `from app.plugins.registry import get_registry` — Core importiert von Plugin Registry. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-231: roles.py — delete_role verwendet roles:write statt roles:delete +- **Datei:** app/routes/roles.py:230 +- **Problem:** `require_permission('roles:write')` für DELETE, aber CORE_PERMISSIONS hat `roles:delete`. User mit write aber ohne delete können Rollen löschen. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-232: groups.py — get_group ohne require_permission +- **Datei:** app/routes/groups.py:100 +- **Problem:** `get_group` hat nur `get_current_user`, kein `require_permission`. Jeder eingeloggte User kann jede Gruppe sehen inkl. Permissions und field_permissions. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-233: groups.py — list_group_members ohne require_permission +- **Datei:** app/routes/groups.py:200 +- **Problem:** `list_group_members` hat nur `get_current_user`. Jeder eingeloggte User kann alle Gruppen-Mitglieder sehen. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-234: groups.py — get_user_groups ohne require_permission +- **Datei:** app/routes/groups.py:240 +- **Problem:** `get_user_groups` hat nur `get_current_user`. Jeder eingeloggte User kann alle Gruppen eines beliebigen Users sehen. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-235: workflows.py — delete_workflow verwendet workflows:write statt workflows:delete +- **Datei:** app/routes/workflows.py:200 +- **Problem:** `require_permission('workflows:write')` für DELETE, aber CORE_PERMISSIONS hat `workflows:delete` nicht — nur `workflows:read` und `workflows:write`. Delete sollte separate Permission haben. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-236: workflows.py — approve_workflow_step auto-approves (sinnlos) +- **Datei:** app/routes/workflows.py:480 +- **Problem:** `create_approval_request()` und sofort `resolve_approval_request(decision='approved')` — Approval wird in selben Request erstellt und approved. Approval ist bedeutungslos. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-237: workflows.py — reject_workflow_step auto-rejects (sinnlos) +- **Datei:** app/routes/workflows.py:530 +- **Problem:** Gleicher Pattern wie ARCH-236 — ApprovalRequest wird erstellt und sofort rejected. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-238: workflows.py — webhook_trigger ohne Auth +- **Datei:** app/routes/workflows.py:580 +- **Problem:** `webhook_trigger` hat keine Auth-Dependency. Jeder mit dem Token kann Workflows triggern. Keine Rate-Limiting, keine IP-Whitelist. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-239: workflows.py — webhook.workflow_id existiert nicht auf Webhook Model +- **Datei:** app/routes/workflows.py:595 +- **Problem:** `webhook.workflow_id if hasattr(webhook, 'workflow_id') else webhook.entity_id` — Webhook Model hat kein `workflow_id` Feld. Der hasattr-Check ist ein Workaround für ein nicht existierendes Feld. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-240: workflows.py — WORKFLOW_TEMPLATES hardcoded +- **Datei:** app/routes/workflows.py:620 +- **Problem:** `WORKFLOW_TEMPLATES` ist eine hardcoded Liste in der Route-Datei. Sollte in Config, DB oder Plugin-Manifest stehen. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-241: dashboard.py — Core→Plugin Import (get_registry) +- **Datei:** app/routes/dashboard.py:22 +- **Problem:** `from app.plugins.registry import get_registry` — Core Route importiert direkt von Plugin Registry. Core darf nicht von Plugins abhängen. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-242: dashboard.py — Company count über Contact.type statt Company Model +- **Datei:** app/routes/dashboard.py:75 +- **Problem:** `Contact.type == 'company'` für Company count. Companies werden als Contacts mit type='company' behandelt, aber es gibt separate companies.py Routes. Doppelarchitektur. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-243: notifications.py — Deprecated Route existiert weiterhin (Doppelarchitektur) +- **Datei:** app/routes/notifications.py:1 +- **Problem:** Route ist deprecated (delegiert an Communication), aber existiert weiterhin und wird in main.py registriert. Legacy Notifications und Communication laufen parallel. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-244: notifications.py — N+1 Query in list_notifications_endpoint +- **Datei:** app/routes/notifications.py:55 +- **Problem:** `entity_permission_service.check_entity_access()` wird für jede Notification in einer Schleife aufgerufen. Bei 100 Notifications = 100 DB-Queries. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-245: compliance.py — Core→Plugin Import (AgentDefinition) +- **Datei:** app/routes/compliance.py:22 +- **Problem:** `from app.plugins.builtins.automation.models import AgentDefinition` — Core Route importiert direkt von Plugin Model. Core darf nicht von Plugins abhängen. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-246: compliance.py — Doppelte Permission-Dependency +- **Datei:** app/routes/compliance.py:120 +- **Problem:** `dependencies=[Depends(require_permission('system:admin'))]` UND `current_user: dict = Depends(require_permission('system:admin'))` — Permission wird zweimal geprüft. Redundant. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-247: compliance.py — _DEFAULT_RETENTION_POLICIES hardcoded +- **Datei:** app/routes/compliance.py:380 +- **Problem:** Retention Policies sind hardcoded in der Route-Datei. Sollte in Config, DB oder Plugin-Manifest stehen. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-248: system_settings.py — dsgvo_export NameError (datetime/timezone nicht importiert) +- **Datei:** app/routes/system_settings.py:200 +- **Problem:** `datetime.now(timezone.utc)` wird verwendet, aber `datetime` und `timezone` sind nicht importiert. Weder am Datei-Anfang noch in der Funktion. Das wird einen NameError geben beim Aufruf. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-249: system_settings.py — update_backup_config ohne Pydantic Schema +- **Datei:** app/routes/system_settings.py:160 +- **Problem:** `body: dict` statt Pydantic Schema. Keine Validierung von backup_enabled, backup_interval, backup_retention_days, backup_destination. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-250: system_dashboard.py — Core→Plugin Import (get_registry) +- **Datei:** app/routes/system_dashboard.py:22 +- **Problem:** `from app.plugins.registry import get_registry` — Core Route importiert direkt von Plugin Registry. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-251: system_dashboard.py — Sync I/O in async Context +- **Datei:** app/routes/system_dashboard.py:200 +- **Problem:** `os.path.exists()`, `os.walk()`, `shutil.disk_usage()` in async Funktion `_get_storage_stats()` — sync I/O blockiert Event Loop. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-252: system_dashboard.py — _send_alert_notifications verschluckt Fehler +- **Datei:** app/routes/system_dashboard.py:260 +- **Problem:** `except Exception: pass` — Fehler beim Senden von Alert-Notifications werden still ignoriert. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-253: audit.py — audit_retention_cleanup verwendet audit:read statt audit:delete +- **Datei:** app/routes/audit.py:160 +- **Problem:** `require_permission('audit:read')` für DELETE-Endpoint. Jeder mit Leserecht kann Audit-Logs löschen. Sollte `audit:delete` oder `system:admin` sein. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-254: audit.py — datetime.utcnow() deprecated +- **Datei:** app/routes/audit.py:165 +- **Problem:** `datetime.utcnow()` ist deprecated in Python 3.12+. Sollte `datetime.now(UTC)` sein. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-255: entity_permissions.py — _check_entity_ownership importiert statische ENTITY_MODELS +- **Datei:** app/routes/entity_permissions.py:100 +- **Problem:** `from app.services.entity_permission_service import ENTITY_MODELS` — statische Liste, neue Plugin-Entities nicht erfasst. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-256: entity_permissions.py — list_entity_registry hardcoded Entity-Liste +- **Datei:** app/routes/entity_permissions.py:250 +- **Problem:** Statische Liste mit 12 Entity-Typen und deutschen Labels. Neue Plugin-Entities werden nicht erfasst. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-257: entity_permissions.py — bulk_share_permissions ohne Pydantic Schema +- **Datei:** app/routes/entity_permissions.py:280 +- **Problem:** `body: dict` statt Pydantic Schema. Keine Validierung von entity_type, entity_ids, principal_type, principal_id, level. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-258: entity_permissions.py — bulk_unshare_permissions ohne Pydantic Schema +- **Datei:** app/routes/entity_permissions.py:300 +- **Problem:** `body: dict` statt Pydantic Schema. Keine Validierung. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-259: entity_permissions.py — Hardcoded deutsche Fehlermeldung +- **Datei:** app/routes/entity_permissions.py:60 +- **Problem:** `"Sie benötigen Admin-Rechte auf diesen Datensatz, um Berechtigungen zu verwalten"` — hardcoded deutsch ohne i18n. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-260: custom_field_definitions.py — Custom Fields an contacts:read gekoppelt +- **Datei:** app/routes/custom_field_definitions.py:25 +- **Problem:** `require_permission('contacts:read')` für generische Custom Field Definitions. Custom Fields sind nicht Contacts-spezifisch. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-261: custom_field_definitions.py — Custom Fields Create an contacts:write gekoppelt +- **Datei:** app/routes/custom_field_definitions.py:45 +- **Problem:** `require_permission('contacts:write')` für Create. Sollte generisch sein (z.B. `custom_fields:write`). +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-262: custom_fields.py — Core→Plugin Import (get_registry) +- **Datei:** app/routes/custom_fields.py:15 +- **Problem:** `from app.plugins.registry import get_registry` — Core Route importiert direkt von Plugin Registry. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-263: custom_fields.py — Route prefix an Contacts gekoppelt +- **Datei:** app/routes/custom_fields.py:20 +- **Problem:** `prefix="/api/v1/contacts"` — Custom Fields sind generisch aber Route ist an Contacts gekoppelt. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-264: custom_fields.py — _collect_custom_field_definitions iteriert über Plugin Manifeste +- **Datei:** app/routes/custom_fields.py:100 +- **Problem:** Core Route iteriert über alle Plugin-Manifeste um Custom Fields zu sammeln. Core sollte nicht wissen dass Plugins Custom Fields haben. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-265: webhooks.py — Inkonsistente Permissions (workflows:read vs automation:read) +- **Datei:** app/routes/webhooks.py:25 vs :80 +- **Problem:** list_webhooks verwendet `workflows:read`, get_webhook verwendet `automation:read`. Inkonsistente Permissions für dasselbe Modul. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-266: webhooks.py — Inkonsistente Permissions (workflows:write vs automation:write) +- **Datei:** app/routes/webhooks.py:45 vs :120 vs :160 +- **Problem:** create_webhook verwendet `workflows:write`, update/delete/test verwenden `automation:write`. Inkonsistent. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-267: saved_views.py — create_saved_view an contacts:read gekoppelt +- **Datei:** app/routes/saved_views.py:100 +- **Problem:** `require_permission('contacts:read')` für generische Saved Views. Saved Views sind nicht Contacts-spezifisch. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-268: saved_views.py — update_saved_view verwendet contacts:read statt contacts:write +- **Datei:** app/routes/saved_views.py:140 +- **Problem:** Update verwendet `contacts:read` statt `contacts:write`. Update sollte Write-Recht erfordern. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-269: saved_views.py — delete_saved_view verwendet contacts:read statt contacts:delete +- **Datei:** app/routes/saved_views.py:170 +- **Problem:** Delete verwendet `contacts:read` statt `contacts:delete`. Delete sollte Delete-Recht erfordern. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-270: saved_views.py — _validate_entity_type importiert statische ENTITY_MODELS +- **Datei:** app/routes/saved_views.py:30 +- **Problem:** `from app.services.entity_permission_service import ENTITY_MODELS` — statische Liste, neue Plugin-Entities nicht erfasst. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-271: saved_filters.py — create_saved_filter an contacts:read gekoppelt +- **Datei:** app/routes/saved_filters.py:80 +- **Problem:** `require_permission('contacts:read')` für generische Saved Filters. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-272: saved_filters.py — delete_saved_filter verwendet contacts:read statt contacts:delete +- **Datei:** app/routes/saved_filters.py:120 +- **Problem:** Delete verwendet `contacts:read` statt `contacts:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-273: saved_filters.py — _validate_entity_type importiert statische ENTITY_MODELS +- **Datei:** app/routes/saved_filters.py:30 +- **Problem:** Gleicher Fehler wie saved_views.py — statische ENTITY_MODELS. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-274: import_export.py — export_data ohne visibility filter +- **Datei:** app/routes/import_export.py:280 +- **Problem:** export_data exportiert alle Contacts/Companies ohne Ownership-Check. user_id und is_system_admin werden übergeben, aber der Service könnte sie nicht filtern. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-275: import_export.py — ineffiziente CSV→XLSX Konvertierung +- **Datei:** app/routes/import_export.py:300 +- **Problem:** export_data parst CSV-Daten zurück zu Rows um sie in XLSX umzuwandeln. Ineffizient — sollte direkt XLSX generieren statt CSV→parse→XLSX. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-276: attachments.py — download_attachment liest gesamte Datei in Memory +- **Datei:** app/routes/attachments.py:100 +- **Problem:** `file_bytes = await storage.read(storage_path)` liest gesamte Datei in Memory. Bei großen Dateien (100MB) kann das OOM verursachen. Sollte StreamingResponse verwenden. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-277: addresses.py — _validate_entity_type importiert statische ENTITY_MODELS +- **Datei:** app/routes/addresses.py:20 +- **Problem:** Gleicher Fehler wie saved_views/saved_filters — statische ENTITY_MODELS. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-278: bank_accounts.py — delete verwendet bank-accounts:write statt bank-accounts:delete +- **Datei:** app/routes/bank_accounts.py:90 +- **Problem:** Delete verwendet `bank-accounts:write` statt `bank-accounts:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-279: bank_accounts.py — IBAN/BIC als Plaintext in DB +- **Datei:** app/routes/bank_accounts.py (implizit über BankAccount Model) +- **Problem:** BankAccount Model speichert IBAN und BIC als Plaintext. Sensitive Daten sollten verschlüsselt werden. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-280: currencies.py — delete verwendet currencies:write statt currencies:delete +- **Datei:** app/routes/currencies.py:70 +- **Problem:** Delete verwendet `currencies:write` statt `currencies:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-281: taxes.py — delete verwendet taxes:write statt taxes:delete +- **Datei:** app/routes/taxes.py:70 +- **Problem:** Delete verwendet `taxes:write` statt `taxes:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-282: sequences.py — delete verwendet sequences:write statt sequences:delete +- **Datei:** app/routes/sequences.py:85 +- **Problem:** Delete verwendet `sequences:write` statt `sequences:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-283: Systematischer Fehler — Alle Core-Routes verwenden :write für DELETE statt :delete +- **Datei:** app/routes/ (currencies, taxes, sequences, bank_accounts, workflows, roles, users) +- **Problem:** Systematischer Architektur-Fehler — alle Core-Routes verwenden `:write` Permission für DELETE-Operationen statt `:delete`. Betroffen: currencies, taxes, sequences, bank_accounts, workflows, roles, users. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-284: delegations.py — delete verwendet permissions:delegations:write statt :delete +- **Datei:** app/routes/delegations.py:80 +- **Problem:** Delete verwendet `permissions:delegations:write` statt `permissions:delegations:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-285: policies.py — delete verwendet permissions:policies:write statt :delete +- **Datei:** app/routes/policies.py:80 +- **Problem:** Delete verwendet `permissions:policies:write` statt `permissions:policies:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-286: owner_transfer.py — Kein Audit-Log für Ownership-Transfer +- **Datei:** app/routes/owner_transfer.py:35 +- **Problem:** transfer_ownership hat kein Audit-Log. Ownership-Transfer ist eine kritische Operation die auditiert werden muss. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-287: api_tokens.py — create_token verwendet mcp:write statt api_tokens:write +- **Datei:** app/routes/api_tokens.py:30 +- **Problem:** `require_permission('mcp:write')` für API Token Creation. API Tokens sind nicht MCP-spezifisch. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-288: api_tokens.py — list_tokens verwendet mcp:read statt api_tokens:read +- **Datei:** app/routes/api_tokens.py:60 +- **Problem:** `require_permission('mcp:read')` für API Token List. Sollte `api_tokens:read` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-289: api_tokens.py — revoke_token verwendet mcp:write statt api_tokens:delete +- **Datei:** app/routes/api_tokens.py:65 +- **Problem:** `require_permission('mcp:write')` für Token Revoke. Sollte `api_tokens:delete` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-290: approvals.py — __import__('sqlalchemy').func.count() in Query +- **Datei:** app/routes/approvals.py:120 +- **Problem:** `__import__('sqlalchemy').func.count()` — dynamischer Import in Query-Construction. Sollte `from sqlalchemy import func` am Datei-Anfang stehen. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-291: backups.py — create_backup ohne Audit-Log +- **Datei:** app/routes/backups.py:60 +- **Problem:** create_backup hat kein Audit-Log. Backup-Erstellung sollte auditiert werden. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-292: backups.py — restore_backup ohne Audit-Log +- **Datei:** app/routes/backups.py:80 +- **Problem:** restore_backup hat kein Audit-Log. Restore ist eine kritische destruktive Operation die auditiert werden muss. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-293: contact_folder_permissions.py — delete verwendet contacts:write statt contacts:delete +- **Datei:** app/routes/contact_folder_permissions.py:80 +- **Problem:** Delete verwendet `contacts:write` statt `contacts:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-294: contact_folders.py — delete_folder verwendet contacts:write statt contacts:delete +- **Datei:** app/routes/contact_folders.py:75 +- **Problem:** Delete verwendet `contacts:write` statt `contacts:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-295: contact_folders.py — reorder_folders ohne Pydantic Schema +- **Datei:** app/routes/contact_folders.py:90 +- **Problem:** `body: list[dict]` statt Pydantic Schema. Keine Validierung. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-296: entity_history.py — restore_from_history manuelle Permission-Prüfung +- **Datei:** app/routes/entity_history.py:100 +- **Problem:** Permission wird manuell geprüft statt `require_permission` Dependency. Inkonsistent mit anderen Routes. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-297: entity_history.py — undo_last_action manuelle Permission-Prüfung +- **Datei:** app/routes/entity_history.py:150 +- **Problem:** Gleiche manuelle Permission-Prüfung wie restore_from_history. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-298: entity_history.py — list_trash ohne require_permission +- **Datei:** app/routes/entity_history.py:200 +- **Problem:** `get_current_user` ohne `require_permission` — jeder eingeloggte User kann Trash sehen ohne spezifische Permission. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-299: errors.py — error.userAgent statt error.user_agent +- **Datei:** app/routes/errors.py:120 +- **Problem:** `error.userAgent` wird verwendet aber Pydantic Model hat `user_agent` (snake_case). AttributeError möglich. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-300: errors.py — Core→Plugin Import (get_contract) +- **Datei:** app/routes/errors.py:130 +- **Problem:** `from app.plugins.builtins.contracts import get_contract` — Core Route importiert direkt von Plugin Contracts. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-301: guests.py — invite_guest ohne Pydantic Schema +- **Datei:** app/routes/guests.py:50 +- **Problem:** `body: dict` statt Pydantic Schema. Keine Validierung von email und name. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-302: guests.py — inkonsistente Transaction-Handling +- **Datei:** app/routes/guests.py:100 +- **Problem:** `db.commit()` und `db.refresh()` in einem Branch (neuer User) aber nicht im anderen (existing User). Inkonsistent. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-303: guests.py — delete_guest verschluckt Fehler +- **Datei:** app/routes/guests.py:170 +- **Problem:** `except Exception: pass` beim Session-Invalidate. Fehler werden ignoriert. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-304: guests.py — settings am Module-Level +- **Datei:** app/routes/guests.py:22 +- **Problem:** `settings = get_settings()` am Module-Level. Settings werden beim Import geladen, nicht bei Request. Änderungen zur Laufzeit werden nicht erkannt. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-305: permission_templates.py — delete verwendet permissions:templates:write statt :delete +- **Datei:** app/routes/permission_templates.py:80 +- **Problem:** Delete verwendet `permissions:templates:write` statt `permissions:templates:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-306: tenants.py — create_tenant ohne Audit-Log +- **Datei:** app/routes/tenants.py:40 +- **Problem:** create_tenant hat kein Audit-Log. Tenant-Erstellung ist eine kritische Operation. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-307: tenants.py — assign_user_to_tenant ohne Audit-Log +- **Datei:** app/routes/tenants.py:65 +- **Problem:** assign_user_to_tenant hat kein Audit-Log. User-Tenant-Zuweisung sollte auditiert werden. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-308: user_preferences.py — delete verwendet user_preferences:write statt :delete +- **Datei:** app/routes/user_preferences.py:160 +- **Problem:** Delete verwendet `user_preferences:write` statt `user_preferences:delete`. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-309: workspaces.py — workspace_context return empty modules wenn kein Workspace +- **Datei:** app/routes/workspaces.py:100 +- **Problem:** Wenn kein Workspace gefunden wird, return `{"modules": []}` — User sieht keine Module. Problem 4 bestätigt. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-310: entity_permission_service.py — Hardcoded deutsche Strings in Notifications +- **Datei:** app/services/entity_permission_service.py:200 +- **Problem:** `'Neue Berechtigung'`, `'Berechtigung entfernt'` — hardcoded deutsch ohne i18n. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-311: entity_permission_service.py — String-Concatenation in SQL für distinct count +- **Datei:** app/services/entity_permission_service.py:380 +- **Problem:** `EntityPermission.entity_type + ":" + EntityPermission.entity_id.cast(String)` für distinct count — ineffizient. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-312: plugin_service.py — activate_plugin prüft Zustand nach registry.activate() +- **Datei:** app/services/plugin_service.py:100 +- **Problem:** `was_already_active = record.active and record.status == 'active'` wird NACH `registry.activate()` geprüft. Problem 1 bestätigt — wenn Plugin bereits aktiv war, werden Permissions trotzdem neu registriert. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-313: plugin_service.py — direkter Zugriff auf _active_plugins +- **Datei:** app/services/plugin_service.py:110 +- **Problem:** `get_permission_registry()._active_plugins.add(name)` greift direkt auf private Attribute zu. Sollte eine public Methode verwenden. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-314: user_service.py — delete_user ohne Audit-Log +- **Datei:** app/services/user_service.py:200 +- **Problem:** delete_user hat kein Audit-Log. User-Löschung sollte auditiert werden. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-315: workspace_service.py — list_workspaces N+1 Query +- **Datei:** app/services/workspace_service.py:40 +- **Problem:** Count users per workspace in einer Schleife — N+1 Query. Bei 10 Workspaces = 10 Count-Queries. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-316: workspace_service.py — hardcoded deutscher String +- **Datei:** app/services/workspace_service.py:580 +- **Problem:** `'Standard-Workspace mit allen Modulen'` — hardcoded deutsch ohne i18n. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-317: role_service.py — delete_role ohne Audit-Log +- **Datei:** app/services/role_service.py:95 +- **Problem:** delete_role hat kein Audit-Log. Rollen-Löschung sollte auditiert werden. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-318: webhook_service.py — __import__('datetime').datetime.utcnow() +- **Datei:** app/services/webhook_service.py:200 +- **Problem:** `__import__('datetime').datetime.utcnow()` — deprecated und ugly. Sollte `datetime.now(UTC)` sein. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-319: webhook_service.py — socket.getaddrinfo sync I/O in async Context +- **Datei:** app/services/webhook_service.py:50 +- **Problem:** `socket.getaddrinfo(hostname, None)` ist sync I/O in async Function. Blockiert Event Loop. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-320: webhook_service.py — secret als Plaintext gespeichert +- **Datei:** app/services/webhook_service.py:150 +- **Problem:** `secret=data.get('secret')` — Webhook Secret wird als Plaintext in DB gespeichert. Sollte verschlüsselt werden. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-321: backup_service.py — datetime.utcnow() deprecated +- **Datei:** app/services/backup_service.py:180 +- **Problem:** `datetime.utcnow()` ist deprecated in Python 3.12+. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-322: backup_service.py — subprocess.run() sync I/O in async Function +- **Datei:** app/services/backup_service.py:200 +- **Problem:** `subprocess.run()` ist sync I/O in async Function. Blockiert Event Loop. Sollte `asyncio.create_subprocess_exec()` verwenden. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-323: owner_transfer_service.py — ENTITY_TABLES statisch +- **Datei:** app/services/owner_transfer_service.py:30 +- **Problem:** `ENTITY_TABLES` ist statisch. Neue Plugin-Entities nicht erfasst. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-325: ai_copilot_service.py — AIConversation/AIMessage mit try/except ImportError +- **Datei:** app/services/ai_copilot_service.py:20 +- **Problem:** `try: from app.models.ai_conversation import AIConversation, AIMessage except ImportError: AIConversation = None` — Models könnten nicht existieren. Wenn None, wird process_query crashen. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-326: ai_copilot_service.py — get_history N+1 Query +- **Datei:** app/services/ai_copilot_service.py:300 +- **Problem:** get_history fragt Messages pro Conversation in einer Schleife ab — N+1 Query. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-327: delegation_service.py — create_delegation verwendet db.commit() +- **Datei:** app/services/delegation_service.py:80 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. Route macht den Commit. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-328: delegation_service.py — delete_delegation verwendet db.commit() +- **Datei:** app/services/delegation_service.py:120 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-329: delegation_service.py — keine Audit-Logs +- **Datei:** app/services/delegation_service.py +- **Problem:** Keine Audit-Logs für Delegation-Erstellung/Update/Löschung. Permission-Delegation ist sicherheitsrelevant. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-330: group_service.py — delete_group ohne Audit-Log +- **Datei:** app/services/group_service.py:160 +- **Problem:** delete_group hat kein Audit-Log. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-331: group_service.py — add_user_to_group ohne Audit-Log +- **Datei:** app/services/group_service.py:200 +- **Problem:** add_user_to_group hat kein Audit-Log. Gruppen-Mitgliedschaft ist sicherheitsrelevant. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-332: group_service.py — remove_user_from_group ohne Audit-Log +- **Datei:** app/services/group_service.py:230 +- **Problem:** remove_user_from_group hat kein Audit-Log. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-333: dedup_service.py — merge_contacts ohne Audit-Log +- **Datei:** app/services/dedup_service.py:280 +- **Problem:** merge_contacts hat kein Audit-Log. Contact-Merge ist kritische Operation. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-334: permission_audit.py — hardcoded deutsche Strings +- **Datei:** app/services/permission_audit.py:40 +- **Problem:** `'Neue Berechtigung'`, `'Berechtigung entfernt'` — hardcoded deutsch ohne i18n. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-335: policy_service.py — ABAC_ALLOWED_FIELDS statisch +- **Datei:** app/services/policy_service.py:60 +- **Problem:** `ABAC_ALLOWED_FIELDS` ist statisch. Neue Plugin-Entities nicht erfasst. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-336: policy_service.py — create_policy verwendet db.commit() +- **Datei:** app/services/policy_service.py:200 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-337: policy_service.py — update_policy verwendet db.commit() +- **Datei:** app/services/policy_service.py:240 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-338: policy_service.py — delete_policy verwendet db.commit() +- **Datei:** app/services/policy_service.py:260 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-339: policy_service.py — apply_policy_filter Group/Role nicht aufgelöst +- **Datei:** app/services/policy_service.py:300 +- **Problem:** Group/Role policies werden nicht richtig aufgelöst — nur `principal_id == user_id` wird geprüft, nicht Group-Membership. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-340: permission_template_service.py — create_template db.commit() +- **Datei:** app/services/permission_template_service.py:80 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-341: permission_template_service.py — update_template db.commit() +- **Datei:** app/services/permission_template_service.py:120 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-342: permission_template_service.py — delete_template db.commit() +- **Datei:** app/services/permission_template_service.py:140 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-343: permission_template_service.py — _evaluate_trigger gibt immer True +- **Datei:** app/services/permission_template_service.py:200 +- **Problem:** `_evaluate_trigger()` gibt immer True zurück. Trigger-Conditions werden nicht ausgewertet. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt + +### ARCH-344: permission_template_service.py — apply_template db.commit() +- **Datei:** app/services/permission_template_service.py:210 +- **Problem:** `db.commit()` in Service — Route kann nicht rollbacken. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-345: contact_folder_service.py — reorder_folders db.commit() in Service +- **Datei:** app/services/contact_folder_service.py:200 +- **Problem:** `db.commit()` in Service — sollte `db.flush()` sein. +- **Schweregrad:** Medium +- **Status:** ⏳ Nicht gefixt + +### ARCH-346: currency_service.py — seed_default_currencies hardcoded Strings +- **Datei:** app/services/currency_service.py:140 +- **Problem:** `code='EUR'`, `name='Euro'`, `symbol='€'` hardcoded. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-347: tax_service.py — seed_default hardcoded deutsche Strings +- **Datei:** app/services/tax_service.py:140 +- **Problem:** `name='Mehrwertsteuer 19%'`, `name='Mehrwertsteuer 7%'` hardcoded deutsch. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-348: address_service.py — hardcoded 'Privat' Label +- **Datei:** app/services/address_service.py:250 +- **Problem:** `label='Privat'` hardcoded deutsch. +- **Schweregrad:** Low +- **Status:** ⏳ Nicht gefixt + +### ARCH-349: tenant_service.py — assign_user_to_tenant ohne Audit-Log +- **Datei:** app/services/tenant_service.py:80 +- **Problem:** assign_user_to_tenant hat kein Audit-Log. User-Tenant-Zuweisung ist sicherheitsrelevant. +- **Schweregrad:** High +- **Status:** ⏳ Nicht gefixt