From 094887f1d38b805ce1e7af8c0abbba5bcccf0dce Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 17 Jul 2026 23:00:28 +0200 Subject: [PATCH] =?UTF-8?q?Pentest=20R120:=20Audit-Log=20/:id=20mit=20nich?= =?UTF-8?q?t-numerischer=20ID=20=E2=86=92=20400=20statt=20500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beim Suchen eines verify-integrity-Endpoints stiess der Pentester auf einen 500er: GET /api/audit-logs/verify matcht GET /:id (das echte Verify ist POST /verify), parseInt("verify") = NaN → Prisma findUnique({ where:{ id: NaN }}) wirft. Gleiche 400-statt-500- Klasse wie R64.1/R104.1. Fix: Number.isNaN-Guard in getAuditLogById, getAuditLogsByCustomer und updateRetentionPolicy → sauberer 400 „Ungültige ID". Co-Authored-By: Claude Opus 4.7 --- backend/src/controllers/auditLog.controller.ts | 12 ++++++++++++ docs/todo.md | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/backend/src/controllers/auditLog.controller.ts b/backend/src/controllers/auditLog.controller.ts index 32a515a1..dc189c93 100644 --- a/backend/src/controllers/auditLog.controller.ts +++ b/backend/src/controllers/auditLog.controller.ts @@ -54,6 +54,12 @@ export async function getAuditLogs(req: AuthRequest, res: Response) { export async function getAuditLogById(req: AuthRequest, res: Response) { try { const id = parseInt(req.params.id); + // Nicht-numerische :id (z.B. GET /audit-logs/verify statt POST /verify) + // landete sonst als NaN in Prisma findUnique → 500. Sauberer 400. + // (Pentest R120 – gleiche „400 statt 500"-Klasse wie R64.1/R104.1.) + if (Number.isNaN(id)) { + return res.status(400).json({ success: false, error: 'Ungültige Audit-Log-ID' }); + } const log = await auditService.getAuditLogById(id); if (!log) { @@ -73,6 +79,9 @@ export async function getAuditLogById(req: AuthRequest, res: Response) { export async function getAuditLogsByCustomer(req: AuthRequest, res: Response) { try { const customerId = parseInt(req.params.customerId); + if (Number.isNaN(customerId)) { + return res.status(400).json({ success: false, error: 'Ungültige Kunden-ID' }); + } const logs = await auditService.getAuditLogsByDataSubject(customerId); res.json({ success: true, data: logs }); @@ -187,6 +196,9 @@ export async function getRetentionPolicies(req: AuthRequest, res: Response) { export async function updateRetentionPolicy(req: AuthRequest, res: Response) { try { const id = parseInt(req.params.id); + if (Number.isNaN(id)) { + return res.status(400).json({ success: false, error: 'Ungültige Policy-ID' }); + } const { retentionDays, description, legalBasis, isActive } = req.body; const policy = await auditService.updateRetentionPolicy(id, { diff --git a/docs/todo.md b/docs/todo.md index 9eac8473..0b0aa0d5 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -97,6 +97,15 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung ## ✅ Erledigt +- [x] **🔧 Pentest R120 – Audit-Log `/:id` mit nicht-numerischer ID → 500 statt 400** + - Pentester stiess beim Suchen eines `verify-integrity`-Endpoints auf + einen 500er. Ursache: `GET /api/audit-logs/verify` matcht `GET /:id` + (das echte Verify ist `POST /verify`), `parseInt("verify")` = NaN → + Prisma `findUnique({ where:{ id: NaN }})` wirft → 500. Gleiche + „400 statt 500"-Klasse wie R64.1/R104.1. + - Fix: `Number.isNaN`-Guard in `getAuditLogById`, + `getAuditLogsByCustomer` und `updateRetentionPolicy` → sauberer 400. + - [x] **🔴 Pentest R120 – CRITICAL IDOR: Vertragsbaum fremder Kunden lesbar** - `GET /api/contracts?tree=true&customerId=` returnte für Portal-User den vollständigen Vertragsbaum eines beliebigen