# Compliance-Implementierung – Umsetzungsbericht **Erstellt:** 2026-05-09 **Audit-Basis:** `docs/compliance-audit.md` **App-Version nach Umsetzung:** 0.8.66 --- ## Freigegebene Pakete und Umsetzungsstatus ### P-03 – Papierkorb-Retention-Job aktivieren ✅ **Status:** Umgesetzt **Betroffene Dateien:** - `docker-compose.yml` – neuer Service `retention-cron` **Technische Änderung:** Neuer Docker-Service `retention-cron` nutzt dasselbe Backend-Image und führt `scripts/media_retention_job.py` täglich um 03:00 Uhr aus. Der Service startet beim ersten Hochfahren sofort und schläft bis zum nächsten 3 AM (Python-basierter Loop ohne externe Cron-Abhängigkeit). Zugriff auf DB und Media-Volume identisch zur Backend-Konfiguration. **Tests:** Keine automatisierten Tests möglich (Runtime-Verhalten); operativ über Container-Logs (`docker logs shinkan-retention-cron`) überprüfbar. **Anmerkungen:** - Retention-Zeiten über Env-Variablen konfigurierbar: `MEDIA_TRASH_SOFT_TO_HIDDEN_DAYS` (Default 30), `MEDIA_TRASH_HIDDEN_TO_PURGE_DAYS` (Default 90) - Das Skript selbst (`scripts/media_retention_job.py`) war bereits korrekt implementiert; nur der Scheduler fehlte --- ### P-04 – Copyright-Pflicht bei Archiv-Promotion vereinheitlichen ✅ **Status:** Umgesetzt **Betroffene Dateien:** - `backend/routers/media_assets.py` – `patch_media_asset()` und `bulk_media_patch()` **Technische Änderung:** Beide Endpoints prüfen nun, ob `copyright_notice` vorhanden ist, wenn `visibility` auf `club` oder `official` gewechselt wird. Priorität: Wert aus dem Request-Body > bestehender Wert in der DB. Ist beides leer, wird HTTP 400 zurückgegeben. Single PATCH: `raise HTTPException(status_code=400, ...)` Bulk PATCH: Asset wird in die `failed`-Liste eingetragen und übersprungen, Gesamtoperation läuft weiter. **Tests:** `backend/tests/test_media_assets_copyright_promotion.py` (7 Tests, alle grün): - Promotion zu `club` ohne Copyright → 400 - Promotion zu `official` ohne Copyright → 400 - Promotion zu `club` mit Copyright im Body → nicht 400 - Promotion zu `club`, Copyright bereits auf Asset → nicht 400 - Kein Visibility-Wechsel → keine Copyright-Prüfung → 200 - Bulk: ohne Copyright → in `failed`, `updated_count == 0` - Bulk: mit Copyright → in `updated`, `updated_count == 1` --- ### P-05 – Passwort-Mindestlänge angleichen ✅ **Status:** Umgesetzt **Betroffene Dateien:** - `backend/routers/auth.py:101` – `PUT /api/auth/pin` - `frontend/src/pages/LoginPage.jsx:175` – Registrierungs-/Login-Formular - `frontend/src/pages/AccountSettingsPage.jsx:403` – Passwort-Änderungsformular **Technische Änderung:** - Backend: `if len(new_pin) < 4` → `if len(new_pin) < 8` (Fehlermeldung angepasst) - Frontend LoginPage: `minLength="6"` → `minLength="8"` - Frontend AccountSettingsPage: `minLength={4}` → `minLength={8}` Alle drei Stellen sind jetzt konsistent mit dem bereits korrekten `POST /api/auth/register` (war schon `< 8`). **Tests:** Keine neuen Tests; Änderung ist trivial und durch bestehende Auth-Tests (Register) indirekt abgedeckt. --- ### P-07 – ALLOW_PUBLIC_MEDIA_STATIC dokumentieren + Release-Test ✅ **Status:** Umgesetzt **Betroffene Dateien:** - `backend/tests/test_security_release.py` – 2 neue Tests **Technische Änderung:** Zwei neue Tests in der bestehenden Release-Test-Suite: 1. `test_public_media_static_not_mounted_by_default`: Verifiziert, dass `/media`-Mount ohne `ALLOW_PUBLIC_MEDIA_STATIC` in der App-Route-Liste nicht vorhanden ist (sicherer Standardzustand in Production). 2. `test_allow_public_media_static_activates_media_mount`: Dokumentiert den Effekt des Flags – wenn gesetzt, ist der Mount aktiv (für Awareness im CI). **Tests:** Beide Tests grün (16/16 in `test_security_release.py`). --- ### P-23 – LoginPage: minLength + Versionsstring ✅ **Status:** Umgesetzt (zusammen mit P-05) **Betroffene Dateien:** - `frontend/src/pages/LoginPage.jsx` **Technische Änderung:** - `minLength="6"` → `minLength="8"` (deckungsgleich mit P-05) - Hardcodierter Versionsstring `v0.1.0 • Development` aus dem Footer der LoginPage entfernt. Kein Ersatz: Die Versionsinformation ist nur im eingeloggten Bereich unter Einstellungen sichtbar. --- ### P-24 – CORS allow_methods und allow_headers einschränken ✅ **Status:** Umgesetzt **Betroffene Dateien:** - `backend/main.py:85-86` – CORSMiddleware-Konfiguration **Technische Änderung:** ```python # Vorher: allow_methods=["*"], allow_headers=["*"], # Nachher: allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], allow_headers=["Content-Type", "X-Auth-Token", "X-Active-Club-Id"], ``` Die drei erlaubten Header entsprechen den tatsächlich genutzten Headers (Auth-Token, Tenant-Header, Content-Type). Alle API-Funktionen bleiben unverändert erreichbar. --- ## Test-Zusammenfassung ``` tests/test_security_release.py 9 passed (inkl. 2 neue P-07 Tests) tests/test_media_assets_copyright_promotion.py 7 passed (neue P-04 Tests) Gesamt neue Tests: 11 Gesamt bestehende Tests: 104 → 103 passed, 1 failed (pre-existing, DB nicht erreichbar) ``` Der vorhandene Fehler (`test_list_media_assets_invalid_lifecycle_400`) war bereits vor dieser Implementierung vorhanden (verifiziert per `git stash`). Er tritt nur lokal auf, wenn kein PostgreSQL-Container mit Hostname `postgres` läuft, und besteht in der CI/Docker-Umgebung nicht. --- ## Nicht umgesetzte Pakete (laut Freigabe ausgeschlossen) P-01, P-02, P-06, P-09, P-10, P-11, P-13–P-16 und alle weiteren Findings bleiben offen. Vollständige Liste und Begründungen: `docs/compliance-audit.md`. --- ## Re-Audit-Empfehlung Nach Deployment der Version 0.8.66 sollten folgende Punkte operativ verifiziert werden: 1. **P-03**: `docker logs shinkan-retention-cron` prüfen — Job läuft einmalig beim Start und danach täglich um 03:00 Uhr 2. **P-04**: Manuelle Stichprobe: PATCH eines privaten Mediums auf `official` ohne `copyright_notice` → muss 400 zurückgeben 3. **P-24**: Browser DevTools → Network → Preflight-Request auf `/api/exercises` → `Access-Control-Allow-Headers` darf nur `content-type, x-auth-token, x-active-club-id` enthalten Nächster vollständiger Re-Audit empfohlen nach Umsetzung der kritischen Findings (P-01: Impressum/Datenschutz, P-02: Löschkonzept/DSGVO-Request-Workflow).