diff --git a/backend/src/controllers/cachedEmail.controller.ts b/backend/src/controllers/cachedEmail.controller.ts index f1de989c..2e82b593 100644 --- a/backend/src/controllers/cachedEmail.controller.ts +++ b/backend/src/controllers/cachedEmail.controller.ts @@ -41,12 +41,31 @@ function parseDateParam(v: unknown): Date | undefined { return isNaN(d.getTime()) ? undefined : d; } +// Pentest 91.1 (LOW, 2026-06-21): `accountId=abc` (→ parseInt = NaN) und +// `accountId=` (leer string ist truthy nach `req.query.accountId ? …`) +// kamen vorher als `NaN` im Service an. `if (NaN)` ist falsy → der +// stressfreiEmailId-Filter wurde komplett übersprungen, und der Vertrag +// zeigte Mails aus ALLEN Postfächern. +// +// Helper akzeptiert nur positive Ganzzahlen; alles andere → undefined, +// das im Service den Filter wegfallen lässt. Wenn wir wollten dass +// invalide Werte 400 werfen statt silent ignoriert zu werden, müssten +// wir das pro Endpunkt entscheiden – die Semantik „Filter weglassen" +// ist hier in Ordnung, weil der `customerId`-/`contractId`-Constraint +// im `where` weiterhin greift (kein Cross-Customer-Leak). +function parsePositiveIntParam(v: unknown): number | undefined { + if (typeof v !== 'string' || v.trim() === '') return undefined; + const n = parseInt(v, 10); + if (!Number.isFinite(n) || n < 1 || !Number.isInteger(n)) return undefined; + return n; +} + // E-Mails für einen Kunden abrufen export async function getEmailsForCustomer(req: AuthRequest, res: Response): Promise { try { const customerId = parseInt(req.params.customerId); if (!(await canAccessCustomer(req, res, customerId))) return; - const stressfreiEmailId = req.query.accountId ? parseInt(req.query.accountId as string) : undefined; + const stressfreiEmailId = parsePositiveIntParam(req.query.accountId); const folder = req.query.folder as string | undefined; // INBOX oder SENT const limit = req.query.limit ? parseInt(req.query.limit as string) : 50; const offset = req.query.offset ? parseInt(req.query.offset as string) : 0; @@ -90,7 +109,7 @@ export async function getEmailsForContract(req: AuthRequest, res: Response): Pro const contractId = parseInt(req.params.contractId); if (!(await canAccessContract(req, res, contractId))) return; const folder = req.query.folder as string | undefined; // INBOX oder SENT - const stressfreiEmailId = req.query.accountId ? parseInt(req.query.accountId as string) : undefined; + const stressfreiEmailId = parsePositiveIntParam(req.query.accountId); const limit = req.query.limit ? parseInt(req.query.limit as string) : 50; const offset = req.query.offset ? parseInt(req.query.offset as string) : 0; @@ -248,7 +267,7 @@ export async function getContractFolderCounts(req: AuthRequest, res: Response): try { const contractId = parseInt(req.params.contractId); if (!(await canAccessContract(req, res, contractId))) return; - const stressfreiEmailId = req.query.accountId ? parseInt(req.query.accountId as string) : undefined; + const stressfreiEmailId = parsePositiveIntParam(req.query.accountId); const counts = await cachedEmailService.getFolderCountsForContract(contractId, stressfreiEmailId); @@ -896,8 +915,8 @@ export async function getTrashEmails(req: AuthRequest, res: Response): Promise