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:
@@ -8,7 +8,7 @@ import { sendEmail, SmtpCredentials, SendEmailParams, EmailAttachment } from '..
|
||||
import { fetchAttachment, appendToSent, ImapCredentials } from '../services/imapService.js';
|
||||
import { getImapSmtpSettings } from '../services/emailProvider/emailProviderService.js';
|
||||
import { decrypt } from '../utils/encryption.js';
|
||||
import { sanitizeNotes, stripHtml, validateContractDocumentType, validateOptionalIsoDate } from '../utils/sanitize.js';
|
||||
import { sanitizeNotes, stripHtml, validateContractDocumentType, validateOptionalIsoDate, assertSafePdf } from '../utils/sanitize.js';
|
||||
import { ApiError } from '../utils/apiError.js';
|
||||
import { logChange } from '../services/audit.service.js';
|
||||
import { ApiResponse, AuthRequest } from '../types/index.js';
|
||||
@@ -1283,6 +1283,9 @@ export async function saveAttachmentTo(req: AuthRequest, res: Response): Promise
|
||||
const filePath = path.join(uploadsDir, newFilename);
|
||||
const relativePath = `/uploads/${targetDir}/${newFilename}`;
|
||||
|
||||
// Pentest 68.1: PDF-Anhänge auf aktive Inhalte scannen (JS/Launch/Embed).
|
||||
assertSafePdf(attachment.content);
|
||||
|
||||
// Datei speichern
|
||||
fs.writeFileSync(filePath, attachment.content);
|
||||
|
||||
@@ -1433,8 +1436,9 @@ export async function saveAttachmentTo(req: AuthRequest, res: Response): Promise
|
||||
} catch (error) {
|
||||
console.error('saveAttachmentTo error:', error);
|
||||
// Detailliertere Fehlermeldung für Debugging
|
||||
const status = error instanceof ApiError ? error.statusCode : 500;
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
res.status(500).json({
|
||||
res.status(status).json({
|
||||
success: false,
|
||||
error: `Fehler beim Speichern des Anhangs: ${errorMessage}`,
|
||||
} as ApiResponse);
|
||||
@@ -2054,6 +2058,9 @@ export async function saveAttachmentAsInvoice(req: AuthRequest, res: Response):
|
||||
const filePath = path.join(uploadsDir, newFilename);
|
||||
const relativePath = `/uploads/invoices/${newFilename}`;
|
||||
|
||||
// Pentest 68.1: PDF-Anhänge auf aktive Inhalte scannen.
|
||||
assertSafePdf(attachment.content);
|
||||
|
||||
// Datei speichern
|
||||
fs.writeFileSync(filePath, attachment.content);
|
||||
|
||||
@@ -2078,8 +2085,9 @@ export async function saveAttachmentAsInvoice(req: AuthRequest, res: Response):
|
||||
} as ApiResponse);
|
||||
} catch (error) {
|
||||
console.error('saveAttachmentAsInvoice error:', error);
|
||||
const status = error instanceof ApiError ? error.statusCode : 500;
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unbekannter Fehler';
|
||||
res.status(500).json({
|
||||
res.status(status).json({
|
||||
success: false,
|
||||
error: `Fehler beim Erstellen der Rechnung: ${errorMessage}`,
|
||||
} as ApiResponse);
|
||||
@@ -2208,6 +2216,9 @@ export async function saveAttachmentAsContractDocument(req: AuthRequest, res: Re
|
||||
const filePath = path.join(uploadsDir, newFilename);
|
||||
const relativePath = `/uploads/contract-documents/${newFilename}`;
|
||||
|
||||
// Pentest 68.1: PDF-Anhänge auf aktive Inhalte scannen.
|
||||
assertSafePdf(attachment.content);
|
||||
|
||||
// Pentest 55.4: Lock vor Schreiben (siehe saveEmailAsContractDocument).
|
||||
const doc = await withContractDocumentLock(contract.id, validatedType, async () => {
|
||||
fs.writeFileSync(filePath, attachment.content);
|
||||
|
||||
Reference in New Issue
Block a user