Pentest 64.1 LOW: ApiError-Klasse, Race-Lock liefert jetzt 400 statt 500

assertNoRecentDuplicateDocument warf einen generischen Error → die
Catch-Blöcke in den drei ContractDocument-Schreibpfaden mappten
das auf 500, obwohl es klar eine 400-Class-Situation (Caller-Fehler:
Duplikat-Submit) ist.

Neuer ApiError-Helper in utils/apiError:
- ApiError(statusCode, message) – einfache Subklasse von Error mit
  explizitem HTTP-Status.

assertNoRecentDuplicateDocument wirft jetzt ApiError(400, ...).

Catch-Blöcke gehärtet (Service-Pattern: `error instanceof ApiError
? error.statusCode : <default>`):
- contract.controller uploadContractDocument: 400-Default bleibt,
  ApiError wird honoriert; bonus: multer-Datei wird bei Reject jetzt
  gelöscht (war vorher orphaned bei Lock-Reject).
- cachedEmail.controller saveEmailAsContractDocument: 500-Default,
  ApiError → 400.
- cachedEmail.controller saveAttachmentAsContractDocument: dito.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 13:51:32 +02:00
parent 518139438e
commit ad81a7c93e
4 changed files with 38 additions and 4 deletions
+18
View File
@@ -0,0 +1,18 @@
/**
* Erlaubt Service-/Helper-Funktionen, einen Fehler mit explizitem HTTP-
* Status nach oben zu reichen. Controller können in ihrem `catch` per
* `instanceof ApiError` den Status auslesen statt pauschal 500 zu liefern.
*
* Pentest 64.1 (LOW, 2026-06-02): Race-Lock (assertNoRecentDuplicate-
* Document) warf einen generischen Error → catch hat 500 zurückgegeben,
* obwohl die Fehlermeldung "Dokument vor wenigen Sekunden bereits
* angelegt" eindeutig eine 400-Class-Situation ist.
*/
export class ApiError extends Error {
readonly statusCode: number;
constructor(statusCode: number, message: string) {
super(message);
this.name = 'ApiError';
this.statusCode = statusCode;
}
}