fix(security): Befunde aus dem Security-Audit beheben #7

Merged
bjoernpoettker merged 10 commits from fix/security-audit into main 2026-11-04 10:03:36 +00:00
10 Commits
Author SHA1 Message Date
bjoernpoettkerandClaude Opus 5.5 d37e02e157 fix: Paperless-PDF auch für den Duplikatvergleich im Mail-Import
Build and Push Multi-Platform Images / build-and-push (push) Successful in 34s
DuplikatVergleichModal lädt das vorhandene Paperless-Dokument über
documents/:id/pdf – auch im Mail-Import (VIEW_MAIL). Die Route verlangte
nur VIEW_INBOX/VIEW_SCANNER, reine Mail-Nutzer bekamen 403.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:48:01 +01:00
bjoernpoettkerandClaude Opus 5.5 fe338271bf fix(security): Symlinks im Scanner, Exportpfad und Belegnummer-URL absichern
- Scanner: Dateien von der Scan-Freigabe werden nur übernommen, wenn sie
  reguläre Dateien in echten Verzeichnissen sind. chokidar folgt keinen
  Symlinks mehr; beim Kopieren über Mount-Grenzen (/mnt/scans → /mnt/data)
  wird mit O_NOFOLLOW geöffnet und nur exklusiv geschrieben. Bisher holte
  ein Symlink wie scan.pdf -> /proc/self/environ die Zieldatei in den
  Posteingang.
- Export: Dateinamen (enthalten u.a. Barcode-Werte) werden auf ein
  Pfadsegment reduziert – „../“ verlässt den FTP-Zielordner nicht mehr.
- Belegnummer: {Jahr}/{Nummer} werden URL-kodiert in BELEGNUMMER_*_URL
  eingesetzt (Nummer stammt aus Request bzw. gedrucktem Barcode);
  belegnummer/release prüft die Typen im Body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:57 +01:00
bjoernpoettkerandClaude Opus 5.5 c28475f71f fix(security): JWT-Audience gegen OIDC_CLIENT_ID prüfen
Bisher prüfte das Backend nur Issuer und Signatur. Ein Access-Token, das
derselbe Authentik-Provider für eine andere Anwendung ausgestellt hat,
wurde ebenfalls akzeptiert.

- JwtStrategy verlangt aud == OIDC_CLIENT_ID (jwt-optionen.ts, getestet
  mit echten RS256-Tokens). Ohne OIDC_CLIENT_ID bleibt die Prüfung aus,
  mit Warnung beim Start.
- docker-compose.yml übergibt OIDC_CLIENT_ID jetzt ans Backend, Standard:
  dieselbe Client-ID wie das Frontend (VITE_OIDC_CLIENT_ID).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:57 +01:00
bjoernpoettkerandClaude Opus 5.5 b11294ec10 fix(security): Path-Traversal in den Mail-Vorschau-Routen
attachments/:attachmentId/pages/:page/{thumb,preview} übernahm die ID
ungeprüft in den Dateipfad – mit `..%2Finbox%2F<uuid>` ließen sich
Scanner-Vorschauen anderer Benutzer aus /mnt/data/inbox lesen.

- Routen wandeln attachmentId und page per ParseIntPipe in Zahlen um.
- EmailPageCacheService nimmt nur positive ganze Zahlen als Anhang-ID und
  Seite an, PageCacheService (Scanner) nur UUIDs als Dokument-ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:57 +01:00
bjoernpoettkerandClaude Opus 5.5 e0a6598b35 refactor: Paperless-Dokumentrouten unter /api/paperless/documents/:id
„inbox“ war doppelt belegt (/api/inbox = Scanner, /api/paperless/inbox =
Paperless-Posteingang) und passte nicht zur Manuell-Seite, die dieselben
Routen nutzt. Umbenannt (ohne Alias, nur das eigene Frontend nutzt sie):

  PUT  paperless/inbox/:id            → PUT  paperless/documents/:id
  GET  paperless/inbox/pdf/:id        → GET  paperless/documents/:id/pdf
  GET  paperless/inbox/preview/:id    → GET  paperless/documents/:id/preview
  GET  paperless/inbox/:id/erechnung  → GET  paperless/documents/:id/erechnung

Dabei:
- Dokumentsuche (GET documents) wieder für den Posteingang (VIEW_INBOX)
  statt nur Admin – die Verknüpfungssuche im Bearbeiten-Dialog nutzt sie;
  die Einschränkung aus dem vorigen Commit hatte sie gebrochen.
- Suche und GET documents/:id liefern nur noch id, Titel, ASN, Datum
  (kein OCR-Volltext, keine Statusfelder).
- documents/:id/pdf auch für VIEW_SCANNER (Duplikatvergleich im Scanner).
- E-Rechnungs-Vorschlag prüft wie das Speichern pro Dokument (Tag 1 bzw.
  Manuell-Tag); Vorschau/PDF bewusst nicht (Suchergebnisse, Duplikate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:57 +01:00
bjoernpoettkerandClaude Opus 5.5 04fca933ee fix(auth): PM_Buchhaltung umfasst den Posteingang
Der Posteingang ist eine abgespeckte Buchhaltung. Bisher bekam
PM_Buchhaltung nur PROCESS_MANUALLY; die Seite „Manuell bearbeiten“
nutzt für Vorschau, PDF, E-Rechnung und Speichern aber Routen mit
VIEW_INBOX – reine Buchhaltungsnutzer konnten dort nichts speichern.

PM_Buchhaltung bekommt jetzt zusätzlich VIEW_INBOX (Backend und Frontend).
permissions.enum.spec.ts vergleicht künftig nicht nur die Gruppennamen,
sondern auch die Berechtigungen pro Gruppe zwischen beiden Kopien.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:43 +01:00
bjoernpoettkerandClaude Opus 5.5 cedd7c78f4 fix(security): Posteingangs-Maske nur für Dokumente im eigenen Arbeitsbereich
PUT paperless/inbox/:id prüft jetzt pro Dokument: Posteingang-Rolle nur
für Dokumente mit Tag 1 (Posteingang), Buchhaltung nur für Dokumente mit
MANUELL_BEARBEITEN_TAG, Admins immer. Bisher ließen sich Mandant (Owner),
Felder und Titel beliebiger Paperless-Dokumente ändern, weil das Backend
mit dem Service-Token arbeitet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:43 +01:00
bjoernpoettkerandClaude Opus 5.5 3f98dfd98f fix(security): Rechteprüfung deny by default, API-Keys nur auf freigegebenen Routen
- PermissionsGuard lehnt Routen ohne @RequirePermissions/@AllowAnyAuthenticated
  ab. API-Keys umgehen keine Rechteprüfung mehr, sondern gelten nur auf
  Routen mit @AllowApiKey (Paperless-Webhook, externer Upload, checksum,
  Agent-Routen des Etikettendrucks).
- Rechte für 24 bisher offene Routen ergänzt, u.a. paperless/documents
  (nur Admin), paperless/documents/:id und Kontonummern (Posteingang/
  manuell), webhook/status (Admin). routen-rechte.spec.ts prüft alle Routen.
- Etiketten-Agent: printed/error nur für abgeholte, offene Jobs (bedingtes
  Update, sonst 409) – ein gedruckter Job kann seine Nummer nicht mehr
  freigeben. Feldwerte nur aus den Eingabefeldern der Vorlage ({number}
  nicht vorgebbar) und URL-kodiert in GET-/Callback-URLs.
- PUT paperless/inbox/:id ignoriert Freigabe (CF15) und Zahlung (CF16)
  ohne die jeweilige Rolle (Funktionstrennung).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:43 +01:00
bjoernpoettkerandClaude Opus 5.5 de9cb09bcd fix(security): Ghostscript mit -dSAFER, Schutz vor PDF-Dekompressionsbomben
- Ghostscript läuft auf PDFs aus Mails und vom Scanner jetzt mit -dSAFER
  statt -dNOSAFER (kein Datei-/Prozesszugriff aus PostScript), mit
  Timeout (60 s je Seite, 10 min je Dokument, SIGKILL) und höchstens
  500 gerenderten Seiten. Temp-Dateien werden auch im Fehlerfall
  entfernt. Ungenutztes getPageCount (brauchte Dateizugriff) entfernt.
- Neuer Vorab-Check ueberschreitetDekompressionsLimit (256 MB, streamend,
  erkennt auch Deflate-Ketten und abgeschnittene Streams). Fingerprint
  (pdf.js) und E-Rechnungs-Extraktion (pdf-lib) laden Bomben nicht mehr;
  eine einzelne Mail konnte das Backend sonst per OOM dauerhaft lahmlegen.
- Verschlüsselte PDFs bekommen keinen Inhalts-Fingerprint mehr ('' =
  nicht vergleichbar), weil pdf.js sie erst intern entschlüsselt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:43 +01:00
bjoernpoettkerandClaude Opus 5.5 bb8bf3714f fix(security): API-Keys nur für Admins, Mailversand ohne Datei-/URL-Zugriff
- ApiKeysController verlangt MANAGE_SETTINGS. Bisher konnte jeder
  OIDC-Benutzer einen API-Key erzeugen, der im PermissionsGuard jede
  Rechteprüfung umgeht (Rechteausweitung zum Admin).
- MailService setzt disableFileAccess/disableUrlAccess für jede Mail.
- send-email im Posteingang prüft den Body streng auf Strings/Zahlen.
  Bisher liest nodemailer bei html: { path: '/proc/self/environ' }
  beliebige Serverdateien ein und verschickt sie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 10:46:43 +01:00