Files
opencrm/backend/src/controllers/provider.controller.ts
T
duffyduck 9cf8c505af Pentest 2026-05-20 Pen-29-Befunde (LOW/INFO)
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:
"j​av​ascript:" 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>
2026-05-20 18:47:44 +02:00

103 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}