freescout2ant/AUDIT_TASKS.md
Claude 7d75d173ea
docs: Code-Audit durchgeführt und 19 priorisierte Aufgaben abgeleitet
Vollständiges Audit aller PHP-Services, Controller, Jobs, Migrationen,
JS-Dateien und offener GitHub-Issues. Aufgaben nach P0-P3 priorisiert:
- P0 (3): Token-Sicherheit, Input-Validierung, XSS-Risiko
- P1 (5): Issue #39 Bug, stille Fehler, Ressourcen-Leaks
- P2 (5): Dead Code, Duplikate, Debug-Ausgaben
- P3 (6): Features #2/#8, Tests, CI/CD, FK-Constraints

https://claude.ai/code/session_0164nAzqDnY1BGQsadTrcxtW
2026-04-14 21:37:29 +00:00

7.8 KiB
Raw Permalink Blame History

Audit & priorisierte Aufgabenliste Ameise Module (freescout2ant)

Datum: 2026-04-14 Scope: Vollständiges Code-Audit aller PHP-Services, Controller, Jobs, Migrationen, JS-Dateien und offener GitHub-Issues.


P0 Kritisch (Sicherheit / Datenverlust)

1. Token-Dateien ohne Dateiberechtigungen gespeichert

Datei: Services/TokenService.php:144-146 Problem: OAuth2-Tokens werden als Klartext-JSON in storage/user_*_ant.txt geschrieben. fopen(..., 'w') setzt keine expliziten Dateiberechtigungen auf Shared-Hosting-Systemen könnten andere Prozesse die Tokens lesen. Lösung: chmod 0600 nach Erstellung setzen oder file_put_contents mit exklusivem Lock verwenden. Langfristig: Tokens verschlüsselt in der Datenbank speichern.

2. Keine Input-Validierung im AJAX-Controller

Datei: Http/Controllers/AmeiseController.php:45 Problem: $inputs = $request->all() wird ohne jegliche Validierung verwendet. Felder wie customer_id, conversation_id, contracts, divisions_data werden direkt in DB-Operationen und JSON-Decoding übernommen. Lösung: Laravel Form-Request-Klassen erstellen (StoreArchiveRequest, SearchUsersRequest) mit strikten Validierungsregeln.

3. XSS-Risiko in JavaScript durch innerHTML mit User-Daten

Datei: Public/js/crm_users.js:172-177 Problem: displayUserDetails() baut HTML via Template-Literals mit selectedUser.record.text, selectedUser.record.first_name etc. direkt in innerHTML. Wenn die CRM-API manipulierte Daten liefert, wird beliebiges HTML/JS ausgeführt. Lösung: textContent statt innerHTML verwenden oder Daten vor dem Einsetzen escapen.


P1 Hoch (Bugs / Zuverlässigkeit)

4. Bug: Antworten werden nach Re-Login nicht archiviert (Issue #39)

Problem: Wenn ein Kunde auf eine archivierte Konversation antwortet, während der Nutzer ausgeloggt war, wird die Antwort auch nach erneutem Login nicht archiviert. Ursache (vermutlich): Der Cron-Job ArchiveThreads dispatcht Jobs pro User, aber wenn das Token abgelaufen ist, schlägt die Archivierung still fehl. Es gibt keinen Retry-Mechanismus. Lösung: Fehlgeschlagene Archivierungen tracken (z.B. Spalte last_error in crm_archive_threads) und bei nächstem erfolgreichen Token erneut versuchen.

5. getAccessToken() kann null zurückgeben stille Fehler

Datei: Services/TokenService.php:42-86 Problem: Bei Exceptions wird null zurückgegeben (kein explizites return). Alle aufrufenden Stellen (CrmApiClient::getAccessToken(), checkTokenError()) prüfen nicht auf null, was zu Bearer null HTTP-Headern führt. Lösung: Explizite Exception werfen oder konsistenten Error-Return (wie den JSON-Redirect) sicherstellen.

6. File-Handle-Leak in TokenService

Datei: Services/TokenService.php:144-146, 184-186 Problem: fopen/fwrite/fclose ohne try-finally wenn fwrite fehlschlägt, bleibt das File-Handle offen. Lösung: file_put_contents() mit LOCK_EX Flag verwenden oder try-finally um die File-Operationen.

7. Imagick-Ressourcen-Leak bei Fehler

Datei: Services/ConversationArchiver.php:101-107 Problem: $img = new \Imagick($path) wird im catch-Block nicht aufgeräumt. Bei Fehler in setImageFormat oder getImagesBlob bleibt die Imagick-Ressource allokiert. Lösung: $img->clear(); $img->destroy(); im finally-Block aufrufen.

8. count() auf möglicherweise nicht-iterablen Wert

Datei: Services/ConversationArchiver.php:151 Problem: count($response) wird aufgerufen, aber fetchUserByEmail() kann ein Array mit ['error' => ...] zurückgeben, ein leeres Array, oder bei Exception einen unerwarteten Typ. Dies kann zu unbeabsichtigten Auto-Archivierungen führen. Lösung: Typ prüfen und sicherstellen, dass $response eine erwartete Kundensuche-Antwort ist (z.B. is_array($response) && isset($response[0]['Id'])).


P2 Mittel (Code-Qualität / Wartbarkeit)

9. CrmService ist ein reiner Wrapper ohne Mehrwert

Datei: Services/CrmService.php Problem: Die Klasse leitet alle Aufrufe 1:1 an TokenService, CrmApiClient und ConversationArchiver weiter. Zusätzlich dupliziert sie getCrmUsers() und getFSUsers() aus dem AmeiseController (Zeile 78-133). Die Klasse wird nirgends mehr verwendet der Controller nutzt die Services direkt. Lösung: CrmService.php entfernen (Dead Code) oder als einzigen Einstiegspunkt konsolidieren.

10. Monolithischer Switch-Block im AJAX-Controller

Datei: Http/Controllers/AmeiseController.php:46-115 Problem: ajax() behandelt alle Actions in einem großen switch-Block. Das erschwert Testbarkeit und Wartung. Lösung: Jede Action in eine eigene Controller-Methode auslagern (searchUsers(), getContracts(), archiveConversation()) mit eigenen Routen.

11. Duplizierte Archivierungslogik

Dateien: AmeiseController.php:95-111 vs. ConversationArchiver.php:128-169 Problem: Die Schleife über Threads + Archive + Attachments existiert nahezu identisch im Controller und im archiveConversationData() des Archivers. Lösung: Controller soll ausschließlich ConversationArchiver aufrufen die Logik sollte nur an einer Stelle existieren.

12. console.log() im Production-JavaScript

Datei: Public/js/crm_users.js:100 Problem: Debug-Ausgabe console.log(data) ist im Production-Code verblieben. Lösung: Entfernen.

13. localStorage-Cache ohne Invalidierung

Problem: Falls CRM-API-Antworten im localStorage gecached werden, gibt es keine TTL oder Invalidierungslogik abgestandene Daten können angezeigt werden. Lösung: Cache mit Zeitstempel versehen oder ganz entfernen.


P3 Niedrig (Verbesserungen / Features)

14. Feature: Kunden-ID aus dem E-Mail-Betreff extrahieren (Issue #2)

Problem: Das alte Plugin konnte Kunden-IDs aus dem E-Mail-Betreff lesen (Zuordnungstyp "kunde"). Dieses Feature fehlt im aktuellen Modul. Lösung: Regex in ConversationArchiver implementieren, der Kunden-IDs aus dem Betreff parst.

15. Feature: Auto-Archivierung bei eindeutigen E-Mail-Adressen (Issue #8)

Problem: Wenn eine E-Mail-Adresse genau einem CRM-Kunden zugeordnet werden kann, soll die Konversation automatisch archiviert werden aktuell nur manuell oder per Cron für bereits archivierte. Lösung: Im ArchiveThreads-Command oder als separaten Job implementieren.

16. Fehlende Foreign-Key-Constraints in Migrationen

Dateien: Database/Migrations/* Problem: crm_archives.conversation_id, crm_archive_threads.thread_id etc. haben keine Foreign-Key-Constraints. Gelöschte Konversationen/Threads hinterlassen verwaiste Einträge. Lösung: Foreign Keys hinzufügen mit onDelete('cascade').

17. Keine automatisierten Tests

Problem: Null Testabdeckung für ein produktives Integrations-Modul. Weder Unit- noch Integrationstests vorhanden. Lösung: PHPUnit-Tests für TokenService, CrmApiClient (mit Mocks), ConversationArchiver und Controller-Endpunkte erstellen.

18. Keine CI/CD Pipeline

Problem: Kein GitHub-Actions-Workflow für Linting, Tests oder Deployment-Checks. Lösung: Workflow für PHP-CS-Fixer, PHPUnit und ggf. PHPStan einrichten.

19. Fehlende Request-Validierungsklassen

Datei: Http/Requests/ (leer) Problem: Keine dedizierten Form-Request-Klassen alle Validierung geschieht ad-hoc oder gar nicht. Lösung: Request-Klassen für jeden Endpunkt erstellen (überschneidet sich mit Aufgabe #2).


Zusammenfassung

Priorität Anzahl Kategorie
P0 3 Sicherheit
P1 5 Bugs & Zuverlässigkeit
P2 5 Code-Qualität
P3 6 Features & Infrastruktur
Gesamt 19

Empfohlene Reihenfolge: P0 → P1 (#4, #5) → P2 (#9, #11) → P1 (#6, #7, #8) → P2 (#10, #12, #13) → P3