Pentest 68.1 (LOW) + 68.2 (INFO): PDF-Active-Content-Filter + Modal-Limit

68.1: Magic-Byte-Check prüfte nur %PDF-. PDFs mit /JavaScript, /JS,
/Launch, /EmbeddedFile, /RichMedia (Flash) kamen durch und wurden
inline ausgeliefert – Browser-Viewer ignorieren JS, Adobe Acrobat
nicht.

- Neuer Helper assertSafePdf(buf) in utils/sanitize.ts mit
  case-sensitivem String-Scan auf die fünf Action-Patterns
  (\b-Word-Boundary verhindert False-Positives bei /JSXForm etc.).
- Neue Middleware pdfUploadSafety.ts mit zwei Varianten:
  requireSafeUploadedPdf (PDF-only) und scanUploadedPdfIfPresent
  (durchwinkt JPG/PNG, scannt nur PDFs).
- Eingehängt in: upload.routes (Magic-Byte-Validator erweitert),
  gdpr.routes Vollmacht-Upload, pdfTemplate.routes Template-Upload,
  contract.routes Vertragsdokumente, cachedEmail.controller
  (saveAttachmentTo, saveAttachmentAsInvoice,
  saveAttachmentAsContractDocument).
- Inline-Vorschau bleibt – Pentester-Empfehlung "disposition=inline
  abschalten" wurde bewusst nicht umgesetzt (löst Acrobat-Risiko
  nicht, bricht aber ~20 UI-Stellen).
- Smoke-Test: 5 Payload-Typen abgelehnt, clean PDF + Non-PDF + JSXForm
  durchgewinkt.

68.2: JpgToPdfModal-Self-DoS – MAX_IMAGES=50, MAX_IMAGE_BYTES=25MB.
This commit is contained in:
2026-06-03 13:18:23 +02:00
parent 30f528596c
commit ec577e6d76
9 changed files with 186 additions and 8 deletions
+31
View File
@@ -97,6 +97,37 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt
- [x] **🔒 Pentest 68.1 (LOW) + 68.2 (INFO): PDF-Inhalts-Validierung + Modal-Limit**
- **68.1 PDF-Active-Content-Filter:** Magic-Byte-Check prüfte bisher
nur `%PDF-`. PDFs mit `/JavaScript`, `/JS`, `/Launch` (externes
Programm), `/EmbeddedFile`, `/RichMedia` (Flash) wurden inline an
den Viewer ausgeliefert Browser-PDF-Viewer (Chrome/Firefox)
ignorieren JS, Adobe Acrobat aber nicht.
- Neuer Helper `assertSafePdf(buf)` in `utils/sanitize.ts`:
String-Scan auf die fünf Action-Pattern (case-sensitive nach
PDF 32000-1:2008 §7.3.5). Wirft `ApiError(415, ...)` bei Treffer.
- Neue Middleware `pdfUploadSafety.ts` mit zwei Varianten:
- `requireSafeUploadedPdf` Datei MUSS PDF sein, sonst 415.
- `scanUploadedPdfIfPresent` durchwinkt JPG/PNG, scannt nur PDFs.
- Eingehängt:
- `upload.routes.ts` (Magic-Byte-Validator erweitert)
- `gdpr.routes.ts` Vollmacht-Upload
- `pdfTemplate.routes.ts` Template-Upload
- `contract.routes.ts` Vertragsdokumente
- `cachedEmail.controller.ts` Email-Anhang-Pfade (3 Stellen:
saveAttachmentTo, saveAttachmentAsInvoice,
saveAttachmentAsContractDocument)
- **Inline-Vorschau bleibt erhalten** das war die explizite
Anforderung (Augen-Button öffnet PDF im neuen Tab). Pentester-
Empfehlung „disposition=inline abschalten" wurde bewusst NICHT
umgesetzt, weil sie das eigentliche Acrobat-Risiko nicht löst
(PDF auf Disk + Doppelklick → Acrobat → JS läuft trotzdem).
- Edge-Case-Test bestätigt: `/JSXForm` und `/JavaScriptFooter` werden
NICHT als JavaScript-Action erkannt (word-boundary `\b` greift).
- **68.2 Modal-Limit:** `JpgToPdfModal` hatte kein Bild-/Größen-Limit.
Jetzt `MAX_IMAGES = 50` + `MAX_IMAGE_BYTES = 25 MB` pro Bild.
UX-Schutz, kein Security-Bug (Self-DoS only).
- [x] **🆕 JPGs → PDF: Button überall bei PDF-Upload**
- Neue Komponente `JpgToPdfModal` (lokal im Browser via `jspdf`,
keine Backend-Round-Trip nötig). Mehrere Bilder hinzufügen per