assertSafePdf: PDF-Streams vor Pattern-Scan ausblenden

Stage-Bug: User lädt zwei Handy-JPGs als PDF hoch → 415 mit
"PDF enthält JavaScript-Action". Die JPEG-Bytes im jsPDF-Output
enthielten zufällig die Byte-Folge "/JavaScript" → Pattern-Match
auf Binär-Daten statt PDF-Struktur.

Fix: stream..endstream-Blöcke vor dem Scan rauspatchen. Echte
PDF-Actions stehen IMMER außerhalb von Streams (Object-Dictionaries),
Binär-Streams (Bilder/Fonts/Komprimiertes) werden ignoriert.

Smoke-Test: jspdf-Style-PDF mit /JavaScript-Bytes im Stream
durchgewinkt, echte /OpenAction /S /JavaScript blockiert,
clean PDF OK.
This commit is contained in:
2026-06-03 17:54:38 +02:00
parent 7c18343a95
commit e792fe4185
2 changed files with 20 additions and 2 deletions
+7 -2
View File
@@ -276,9 +276,14 @@ export function assertSafePdf(buf: Buffer): void {
if (buf.length < 5 || buf.subarray(0, 5).toString('latin1') !== '%PDF-') {
return; // keine PDF → andere Validatoren zuständig
}
const content = buf.toString('latin1');
// Stream-Inhalte (Bilder/Fonts/Komprimiertes) aus dem Scan rausnehmen.
// Jpeg-Bytes können zufällig "/JavaScript" enthalten → false-positive
// bei jsPDF-generierten PDFs mit eingebetteten Fotos (stage-Bug
// 2026-06-03). Echte aktive PDF-Inhalte stehen IMMER im PDF-
// Object-Stream (außerhalb von `stream..endstream`-Blöcken).
const scanTarget = buf.toString('latin1').replace(/stream\s[\s\S]*?endstream/g, '');
for (const { pattern, label } of PDF_DANGER_PATTERNS) {
if (pattern.test(content)) {
if (pattern.test(scanTarget)) {
throw new ApiError(
415,
`PDF enthält nicht erlaubte aktive Inhalte (${label}). Bitte ohne JavaScript / Auto-Actions / eingebettete Dateien hochladen.`,