diff --git a/backend/src/services/contract.service.ts b/backend/src/services/contract.service.ts index 1ed62d8d..25d12edd 100644 --- a/backend/src/services/contract.service.ts +++ b/backend/src/services/contract.service.ts @@ -322,6 +322,17 @@ interface ContractCreateData { }; } +// Erlaubte Mobilfunknetze. Server-seitige Whitelist, damit über einen +// handgebauten Request kein beliebiger String in mobileNetwork landet +// (das Frontend-Dropdown beschränkt schon, der Server vertraut ihm aber +// nicht). Unbekannte/leere Werte werden zu null normalisiert. +const ALLOWED_MOBILE_NETWORKS = new Set(['TELEKOM', 'VODAFONE', 'TELEFONICA']); +function normalizeMobileNetwork(value: unknown): string | null { + if (typeof value !== 'string') return null; + const v = value.trim().toUpperCase(); + return ALLOWED_MOBILE_NETWORKS.has(v) ? v : null; +} + export async function createContract(data: ContractCreateData) { const { energyDetails, @@ -387,7 +398,7 @@ export async function createContract(data: ContractCreateData) { mobileDetails: { create: { requiresMultisim: mobileDetails.requiresMultisim, - mobileNetwork: mobileDetails.mobileNetwork ?? null, + mobileNetwork: normalizeMobileNetwork(mobileDetails.mobileNetwork), dataVolume: mobileDetails.dataVolume, includedMinutes: mobileDetails.includedMinutes, includedSMS: mobileDetails.includedSMS, @@ -623,6 +634,11 @@ export async function updateContract( if (mobileDetails) { const { simCards, ...mobileData } = mobileDetails; + // Whitelist auf das Netz anwenden, bevor mobileData per Spread an + // Prisma geht (Update-Pfad reicht sonst beliebige Strings durch). + if ('mobileNetwork' in mobileData) { + mobileData.mobileNetwork = normalizeMobileNetwork(mobileData.mobileNetwork); + } const existing = await prisma.mobileContractDetails.findUnique({ where: { contractId: id }, }); diff --git a/docs/todo.md b/docs/todo.md index f67e646b..9449c920 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -97,6 +97,14 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung ## ✅ Erledigt +- [x] **🔒 Mobilfunknetz: server-seitige Whitelist (Pentest-INFO)** + - `mobileNetwork` akzeptierte serverseitig jeden (stripHtml-bereinigten) + String – das Frontend-Dropdown beschränkte nur clientseitig. Jetzt + Whitelist `TELEKOM|VODAFONE|TELEFONICA` in `contract.service.ts` + (`normalizeMobileNetwork`), angewandt in Create- UND Update-Pfad + (Update-Spread wurde vorher ungefiltert durchgereicht). Unbekannte/ + leere Werte → null. + - [x] **🔒 Rate-Limiting: IPv6-Bypass-Härtung (ipKeyGenerator)** - Die Rate-Limiter mit eigenem `keyGenerator` (`loginRateLimiter`, `staffPasswordReAuthLimiter`) keyten auf die **volle** `req.ip`.