9cf8c505af
28.1 Restarbeit (URI-Schemata):
DANGEROUS_URI_SCHEMES jetzt vollstaendig – blob:, about:, ws:, wss:,
ldap:, dict: ergaenzt. http(s):, mailto:, tel: bewusst nicht
geblockt (legitime URLs in Notizfeldern).
29.1 Cyrillic-Homoglyph:
"jаvascript:" mit U+0430 lief durch die Regex. HOMOGLYPH_TO_ASCII-
Map (а→a, е→e, о→o, …, 13 Eintraege) wird VOR dem Scheme-Strip
angewendet.
29.2 Percent-Encoding:
"java%73cript:" und "java%2573cript:" umgingen den Filter.
percentDecode() laeuft jetzt iterativ bis zu 5 Runden.
29.3 Zero-Width-Joiner:
"javascript:" mit U+200B/200C/200D etc. zerteilte die Regex-
Matches. ZERO_WIDTH_CHARS-Regex strippt alle unsichtbaren Unicode-
Steuerzeichen, bevor irgendwas anderes laeuft.
28.3 Partial (PDF-Validierung tiefer):
Magic-Bytes allein reichten nicht – "%PDF-1.4\n#!/bin/bash" kam
durch. Jetzt zusaetzlich %%EOF-Marker in den letzten 1 KB +
Pattern-Scan der ersten 4 KB auf #!/, <script, <?php, <%, "MZ "
(PE-Header).
29.4 Email-Format-Validator:
neuer isValidEmail() lehnt Whitespace/Newlines (SMTP-Header-
Injection-Vektor) und Format-Muell ab. Verdrahtet in
create/update Customer + User + updatePortalSettings.
29.5 GET /api/providers/email 500 -> 404:
parseInt("email") = NaN, Prisma crashte. Controller validiert jetzt
Number.isFinite(id) und liefert 404.
Live-verifiziert auf dev: 13 Test-Cases (alle Schema-Varianten,
Homoglyphe, Percent, ZWJ, PDF-Validierung, Email-Format,
/providers/email) – alle erwarteten Antworten.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { Request, Response } from 'express';
|
||
import * as providerService from '../services/provider.service.js';
|
||
import { logChange } from '../services/audit.service.js';
|
||
import { ApiResponse } from '../types/index.js';
|
||
|
||
export async function getProviders(req: Request, res: Response): Promise<void> {
|
||
try {
|
||
const includeInactive = req.query.includeInactive === 'true';
|
||
const providers = await providerService.getAllProviders(includeInactive);
|
||
res.json({ success: true, data: providers } as ApiResponse);
|
||
} catch (error) {
|
||
res.status(500).json({
|
||
success: false,
|
||
error: 'Fehler beim Laden der Anbieter',
|
||
} as ApiResponse);
|
||
}
|
||
}
|
||
|
||
export async function getProvider(req: Request, res: Response): Promise<void> {
|
||
try {
|
||
// `req.params.id` ist Pfad-Segment – bei /api/providers/email landet
|
||
// hier der String "email", den parseInt zu NaN macht. Ohne Validierung
|
||
// fuhr Prisma dann gegen `WHERE id = NaN` und warf 500.
|
||
// Pentest 2026-05-20, 29.5: explizit 404 statt 500. Andere Sub-Routes
|
||
// wie /api/providers/<id>/tariffs greifen weiter wie gehabt.
|
||
const id = parseInt(req.params.id, 10);
|
||
if (!Number.isFinite(id) || id < 1) {
|
||
res.status(404).json({
|
||
success: false,
|
||
error: 'Anbieter nicht gefunden',
|
||
} as ApiResponse);
|
||
return;
|
||
}
|
||
const provider = await providerService.getProviderById(id);
|
||
if (!provider) {
|
||
res.status(404).json({
|
||
success: false,
|
||
error: 'Anbieter nicht gefunden',
|
||
} as ApiResponse);
|
||
return;
|
||
}
|
||
res.json({ success: true, data: provider } as ApiResponse);
|
||
} catch (error) {
|
||
res.status(500).json({
|
||
success: false,
|
||
error: 'Fehler beim Laden des Anbieters',
|
||
} as ApiResponse);
|
||
}
|
||
}
|
||
|
||
export async function createProvider(req: Request, res: Response): Promise<void> {
|
||
try {
|
||
const provider = await providerService.createProvider(req.body);
|
||
await logChange({
|
||
req, action: 'CREATE', resourceType: 'Provider',
|
||
resourceId: provider.id.toString(),
|
||
label: `Anbieter ${provider.name} angelegt`,
|
||
});
|
||
res.status(201).json({ success: true, data: provider } as ApiResponse);
|
||
} catch (error) {
|
||
res.status(400).json({
|
||
success: false,
|
||
error: error instanceof Error ? error.message : 'Fehler beim Erstellen des Anbieters',
|
||
} as ApiResponse);
|
||
}
|
||
}
|
||
|
||
export async function updateProvider(req: Request, res: Response): Promise<void> {
|
||
try {
|
||
const provider = await providerService.updateProvider(parseInt(req.params.id), req.body);
|
||
await logChange({
|
||
req, action: 'UPDATE', resourceType: 'Provider',
|
||
resourceId: provider.id.toString(),
|
||
label: `Anbieter ${provider.name} aktualisiert`,
|
||
});
|
||
res.json({ success: true, data: provider } as ApiResponse);
|
||
} catch (error) {
|
||
res.status(400).json({
|
||
success: false,
|
||
error: error instanceof Error ? error.message : 'Fehler beim Aktualisieren des Anbieters',
|
||
} as ApiResponse);
|
||
}
|
||
}
|
||
|
||
export async function deleteProvider(req: Request, res: Response): Promise<void> {
|
||
try {
|
||
const providerId = parseInt(req.params.id);
|
||
const provider = await providerService.getProviderById(providerId);
|
||
await providerService.deleteProvider(providerId);
|
||
await logChange({
|
||
req, action: 'DELETE', resourceType: 'Provider',
|
||
resourceId: providerId.toString(),
|
||
label: `Anbieter ${provider?.name || providerId} gelöscht`,
|
||
});
|
||
res.json({ success: true, message: 'Anbieter gelöscht' } as ApiResponse);
|
||
} catch (error) {
|
||
res.status(400).json({
|
||
success: false,
|
||
error: error instanceof Error ? error.message : 'Fehler beim Löschen des Anbieters',
|
||
} as ApiResponse);
|
||
}
|
||
}
|