R145-Hygiene: limit-Floor + getContracts fail-closed (Konsistenz)

Pentester R145 (nice-to-have, keine Findings):
1) listAll: limit bekommt einen Floor (Math.max(1, ...)) - limit=-5
   ergab vorher take:-5 an Prisma. page ebenso auf >=1 geklemmt.
2) getContracts nutzte dasselbe 'if (isCustomerPortal && customerId)'-
   Muster und war NICHT fail-closed. Jetzt konsistent zu listAll:
   - Controller: Portal-Token immer gescoped (ohne customerId -> []).
   - Service getAllContracts: 'if (customerIds)' statt '.length > 0',
     damit ein leeres Array strikt auf IN () filtert (0 Treffer) statt
     durchzufallen. Einziger Caller ist der Contract-Controller ->
     keine Regression fuer den Normalfall.

Verifiziert: Staff -> alle; Portal customerIds=[] -> 0 Vertraege/Belege.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 11:32:21 +02:00
co-authored by Claude Opus 4.8
parent 4c5548ea3c
commit 8d93c1767b
3 changed files with 20 additions and 14 deletions
+5 -2
View File
@@ -21,8 +21,11 @@ export async function getAllContracts(filters: ContractFilters) {
const where: Record<string, unknown> = {};
// Entweder einzelne customerId ODER Liste von customerIds (für Kundenportal)
if (customerIds && customerIds.length > 0) {
// Entweder Liste von customerIds (Kundenportal, fail-closed) ODER einzelne
// customerId (Staff-Filter). Fail-closed (Pentest R145): ist customerIds
// gesetzt auch als LEERES Array wird strikt darauf gefiltert (`IN ()`
// → 0 Treffer). Nur `undefined` (Staff) überspringt den Filter.
if (customerIds) {
where.customerId = { in: customerIds };
} else if (customerId) {
where.customerId = customerId;