Pentest 56.1/56.2/56.3/56.4/56.5: Ownership-Checks + InvoiceType-Validierung
56.1 HIGH (IDOR auf Upload-Endpoints):
- /upload/bank-cards/:id (POST/DELETE): canAccessBankCard +
Existenz-Check, multer-Datei wird bei Reject sauber aufgeräumt.
- /upload/documents/:id (POST/DELETE): canAccessIdentityDocument
+ Existenz-Check + Cleanup.
- /upload/customers/:id/{business-registration,commercial-register,
privacy-policy} (POST/DELETE): canAccessCustomer + Cleanup.
- /upload/invoices/:id (POST/DELETE): canAccessContract über
Invoice→Contract-Resolve + Cleanup.
56.2 HIGH (IDOR + Consent-Eskalation bei privacy-policy):
- Vor dem upsert auf alle 4 CustomerConsent-Einträge (=GRANTED)
läuft jetzt canAccessCustomer. Portal-Vertreter ohne Vollmacht
oder Mitarbeiter mit anderer Customer-Beschränkung kommen
damit nicht mehr durch.
56.3 LATENT (updateContract / deleteContract):
- Defense-in-Depth: canAccessContract jetzt explizit im Controller,
nicht nur über die Route-Permission.
56.4 MEDIUM (invoiceType ungeprüft in addInvoiceByContract):
- Neuer assertValidInvoiceType-Helper mit Whitelist
['INTERIM','FINAL','NOT_AVAILABLE'] in addInvoice,
updateInvoice und addInvoiceByContract. updateInvoice nur
bei explizit gesetztem Wert; addInvoiceByContract zusätzlich
die fehlende Required-Field-Validierung ergänzt.
56.5 LOW (GDPR-Löschanfragen ohne Ownership-Check):
- POST /api/gdpr/deletions liest customerId jetzt aus dem Body
(Route hat kein :id-Segment), validiert auf positive Zahl und
ruft canAccessCustomer auf, bevor die Löschanfrage erstellt wird.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,6 +181,11 @@ export async function createContract(req: AuthRequest, res: Response): Promise<v
|
||||
export async function updateContract(req: AuthRequest, res: Response): Promise<void> {
|
||||
try {
|
||||
const contractId = parseInt(req.params.id);
|
||||
// Pentest 56.3 (latent, 2026-06-01): Defense-in-Depth –
|
||||
// canAccessContract explizit aufrufen, statt sich nur auf die
|
||||
// Route-Permission zu verlassen. Portal-User mit kompromittierter
|
||||
// Token-Permission würden sonst beliebige Verträge editieren können.
|
||||
if (!(await canAccessContract(req, res, contractId))) return;
|
||||
// Vorherigen Stand laden für Audit-Vergleich
|
||||
const before = await prisma.contract.findUnique({
|
||||
where: { id: contractId },
|
||||
@@ -264,6 +269,8 @@ export async function updateContract(req: AuthRequest, res: Response): Promise<v
|
||||
export async function deleteContract(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const contractId = parseInt(req.params.id);
|
||||
// Pentest 56.3 (latent): Defense-in-Depth – Ownership-Check vor Delete.
|
||||
if (!(await canAccessContract(req as AuthRequest, res, contractId))) return;
|
||||
const contract = await prisma.contract.findUnique({ where: { id: contractId }, select: { contractNumber: true, customerId: true } });
|
||||
await contractService.deleteContract(contractId);
|
||||
await logChange({
|
||||
|
||||
Reference in New Issue
Block a user