## Ergebnis Read-only-Audit auf sauberem `main` bei SHA `d666b9a4abb54c5adcb378a22e09e007f09f0d47`. Keine Produktionsdaten geöffnet, keine Dateien verändert. ## Konkreter Erweiterungsplan ### 1. Authentifizierter Upload und Quarantäne - **`scripts/health/health_dashboard_server.py`** - Neue Dokumentroute neben `CAPTURE_UPLOAD_ROUTE` definieren. - `Handler.do_POST()` und neue Handler-Methode analog zu `Handler._handle_capture_upload()` erweitern. - Bestehende Sicherheitsbausteine wiederverwenden: - `_v5_principal_authenticated()` / Browser-Session - `origin_matches_request()` - `issue_csrf_token()` / `consume_csrf_token()` - Host-Prüfung in `do_POST()` - `write_action_payload()` für die spätere Verarbeitung - Upload-Body begrenzen und gestreamt in private Quarantäne schreiben; nicht wie aktuell vollständig via `self.rfile.read(length)` in RAM laden. - Vertrag: nur PDF/JPEG/PNG; HEIC ausschließlich bei vollständig lokaler, sicherer Decoder-Unterstützung, sonst fail-closed. - **`scripts/health/dashboard_v5/capture_media.py`** - Reuse: `_private_directory()`, `_read_private()`, No-Follow-, Owner-/Mode-, Größen-, Hash- und Metadatenprüfung. - Nicht direkt erweitern als „Bildanhang“: neuer dokumentbezogener Helper ist sauberer, weil PDF, Seitenlimit, andere Größenlimits und keine Raster-Rekodierung gelten. - Quarantänemetadaten müssen internen Token, Magic-MIME, Bytezahl, Hash und ggf. Seitenzahl enthalten; keine Browser-Dateinamen. ### 2. Action-Inbox und Worker-Bestätigung - **`scripts/health/health_dashboard_server.py`** - `write_action_payload()` als atomare `0600`-Queue wiederverwenden. - Neuer strikt versionierter Action-Contract, z. B. `document_ingest`, mit Quarantäne-Token und Idempotency-Key. - Status-GET analog `capture_processing_status()` ergänzen; Antwort muss `queued`, `processing`, `completed` und `rejected` unterscheiden. - **`scripts/health/health_dashboard_action_worker.py`** - Erweitern: - `validate_payload()` - `apply_action()` - `main()` - Reuse: - `load_action()` mit `O_NOFOLLOW`, Owner/Mode/Size und Duplicate-Key-Rejection - `.failed`-Behandlung - Receipt-Muster aus `write_capture_receipt()` - Neuer Apply-Pfad muss: 1. Quarantänedatei descriptorbasiert erneut validieren, 2. DB-Transaktion starten, 3. Original atomar in private Dokumentablage promoten, 4. Dokument-/Jobdatensatz erstellen, 5. Extraktion ausführen bzw. sicher anstoßen, 6. nur ungeprüfte Kandidaten erzeugen, 7. niemals kanonische medizinische Tabellen aktualisieren. ### 3. Schema und additive Migration - **Neues Schema-Modul nach Muster** - `scripts/health/dashboard_v5/sprint6h_b_schema.py` - Muster: `DDL`, `apply_schema()`, `assert_schema()`, Migration-Marker. - Benötigt getrennte Zustände für: - Upload/Quarantäne/Promotion - Extraktion - inhaltliche Prüfung - FTS-Freigabe - Kandidatenprüfung - Sinnvolle additive Tabellen: Dokument-Ingest-Job/Action-Log und maschinelle Dokumentkandidaten mit ausschließlich `pending/confirmed/rejected`; keine Trigger oder automatische Writes auf `laborwerte`, Medikamente, Diagnosen etc. - Bestehende `document_extractions` und `document_extraction_sections` können weiterverwendet werden. - **Neue Migration nach Muster** - `scripts/health/migrate_sprint6h_b_schema.py` - Reuse: explizites `--db`, Backup, Copy-first-Probe, Idempotenz, `integrity_check`, `foreign_key_check`, Restore-Proof. - `database/schema.sql` anschließend nur additiv als kanonische Neuinstallationsdefinition synchronisieren. ### 4. Extraktion - **`scripts/health/local_document_extraction.py`** - Wiederverwendbar: - `useful()` - `pdftotext()` - Reihenfolge in `extract_pdf()`: `pdftotext` → `ocrmypdf`/Tesseract - `sections()` sowie Engine-/Confidence-/Repetition-Metadaten - `document_extractions.canonical_data_status='not_promoted'` - Zu härten: - `safe_original()`, `file_hash()` und Subprozessaufrufe sind pfadbasiert und damit nicht ausreichend TOCTOU-sicher. - Worker muss einen geprüften/pinnierten Descriptor oder eine ausschließlich worker-eigene, bereits atomar promotete Datei verwenden. - PDF-Magic, strukturelle Öffnung und maximales Seitenlimit vor teurem OCR prüfen. - JPEG/PNG benötigen lokalen Tesseract-Pfad; HEIC nur mit sicher verfügbarem lokalen Decoder. - Fehlerzustände wie Formatfehler, Seitenlimit, Timeout und OCR-Fehler getrennt persistieren. - **Nicht wiederverwenden** - `scripts/health/analyze_document.py`: Vision/Ollama-Pfad und schwache Dateiprüfung passen nicht zur lokalen, fail-closed Grenze. - `scripts/health/process_all_health_documents.py`: breite Pfadsuche und direkte Updates an `dokumente`; nicht für den Upload-Worker verwenden. ### 5. FTS und Review-Grenze - **Geprüfte FTS** - `scripts/health/document_fts_migrate.py` - `rebuild()` und `iter_document_chunks()` wiederverwenden. - Bestehender Vertrag indexiert nur `dokumente.review_status='geprueft'`. - `scripts/health/dashboard_v5/document_chunks.py` - Als gemeinsamer Normalisierungs-/Chunk-Vertrag für Preview und FTS beibehalten. - `scripts/health/dashboard_v5/read_api.py` - `_record_document_rows()`, `_fts_tokens()`, `_fts_literal_query()` und `_record_document_matches()` besitzen bereits den aktuellen Review-Join. - **Wichtiger bestehender Leak/Blocker** - `_machine_search_documents()` sucht `health_document_machine_fts` **ohne aktuellen Review-Status-Filter**. - `_search()` mischt diese Treffer in die globale Dokumentensuche. - Damit können ungeprüfte maschinelle Inhalte aktuell außerhalb eines Review-Inbox-Vertrags erscheinen. - Für 6I-A: machine FTS nur in der authentifizierten Review-Inbox verwenden oder dort zwingend mit expliziter Pending-Policy abfragen; globale Suche, Matches, Reports und medizinische Zusammenfassungen bleiben geprüft-only. ### 6. Originals und ungeprüfte Vorschau - **`scripts/health/dashboard_v5/document_originals.py`** - Direkt wiederverwenden: - `configured_original_roots()` - `_open_regular_beneath()` - `probe_original()` - Magic-/Size-Prüfung und generische Downloadnamen. - Private Originalablage als zusätzlichen konfigurierten Root aufnehmen. - **`scripts/health/health_dashboard_server.py`** - `open_safe_original()` und Route `/api/v1/documents//original` unterstützen bereits geprüfte und ungeprüfte Dokumente, descriptorbasiertes Streaming sowie `no-store`, `nosniff`, `no-referrer`. - Legacy-Routen `/health-doc/` und `/health-report/...` bleiben separate, ältere Vertrauensgrenzen und müssen für einen globalen Sicherheitsclaim inventarisiert/gehärtet werden. - **`scripts/health/dashboard_v5/read_api.py`** - `_record_document_extracted_preview()` ist die richtige Review-Inbox-Grenze. - Derzeit liest sie jedoch `dokumente.extrahierte_inhalte`; die neue Pipeline schreibt `document_extraction_sections`. Preview muss daher auf die aktuelle ungeprüfte Extraktion und den gemeinsamen Chunk-Vertrag umgestellt werden. - `_record_document_detail()` und `_record_document_matches()` müssen weiterhin `reviewed=True` verlangen. ### 7. Kandidaten und Action-Inbox-API - Im bestehenden Schema gibt es **keinen persistenten generischen Dokumentkandidaten-Contract**. - Bestehende „candidate“-Verwendungen in Labor-Reconciliation sind Report-/Staging-Strukturen, keine passende Inbox-Tabelle. - Neue Kandidaten müssen: - ausschließlich maschinell/ungeprüft sein, - Extraktion und Quellsektion referenzieren, - unabhängig von `dokumente.review_status` geprüft werden, - nur per bestätigter Worker-Action `confirmed/rejected` werden, - auch bei Bestätigung **keine kanonische medizinische Auto-Übernahme** auslösen. - Read-API-Erweiterung in `dispatch_api()` mit eigener Review-Inbox-Route; opaque IDs, keine Pfade/Hashes/numerischen DB-IDs. ## Tests, die erweitert/neu gespiegelt werden sollten - `tests/test_dashboard_v5_sprint6h_b.py`: Quarantäne, Symlink, Mode, Hash/Metadaten, Worker/Receipt/Idempotenz. - `tests/test_dashboard_v5_sprint6f_b_pipelines.py`: Extraktionsstatus, OCR-Fallback, Review-/Canonical-Trennung. - `tests/test_dashboard_v5_sprint6e1_original.py`: Pending-Original, HEAD/GET-Revalidierung, Parent-/Leaf-Symlinks, Magic/Size. - Neue fokussierte 6I-A-Tests: PDF-Seitenlimit, TOCTOU-Austausch, JPEG/PNG-OCR, HEIC fail-closed, stale machine-FTS exclusion, Preview aus `document_extraction_sections`, Kandidatenbestätigung ohne kanonische Tabellenänderung. ## Hauptblocker - Kein vorhandener Dokument-Upload-/Kandidaten-Action-Contract. - Aktueller Upload-Handler ist bildspezifisch und liest den gesamten Body in den Speicher. - `local_document_extraction.py` ist pfadbasiert und nicht TOCTOU-sicher; kein Seitenlimit, keine Bildextraktion. - Zwei FTS-Systeme (`health_document_fts` geprüft, `health_document_machine_fts` ungeprüft); ungeprüfte Machine-FTS wird aktuell global exponiert. - Preview-Quelle (`dokumente.extrahierte_inhalte`) und neue Extraktionsquelle (`document_extraction_sections`) sind nicht verbunden. - `process_all_health_documents.py` schreibt direkt Dokumentmetadaten/Text und ist deshalb kein sicherer Worker-Baustein. - Systemd-Units benötigen neue private Dokument-Quarantäne-/Originalpfade in `ReadWritePaths`; Server darf nur Quarantäne/Inbox schreiben, Worker DB und Originalablage. **Änderungen:** keine. Git-Tree blieb sauber.