From b023eab1f46593ae88dd13d277a7ec17ba38934f Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Thu, 9 Jul 2026 21:42:08 +0100 Subject: [PATCH] fix(http): return 401 (not 500) on expired/rejected session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Authenticate/RequireCSRF write a 401 then raise ESessionRejected; when it reached the dispatcher catch-all, the generic `on E: Exception` overwrote it with a 500. Added `on ESessionRejected do Exit` before the generic clause in both dispatchers (GET + Other) — one place, covers every handler whether or not it wraps Authenticate. Root cause, not per-handler patch. ponytail: runtime check only (expired token → 401) — no Delphi unit harness. Co-Authored-By: Claude Opus 4.8 --- CODE_AUDIT.md | 2 +- delphi-backend/Source/PM.HTTPServer.pas | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CODE_AUDIT.md b/CODE_AUDIT.md index 8261ddd..b153b66 100644 --- a/CODE_AUDIT.md +++ b/CODE_AUDIT.md @@ -200,7 +200,7 @@ entries), problématique au-delà. **Recommandation** : batcher les déchiffrements (Promise.all par lots), et si besoin paginer côté serveur pour la vue grille. -### 2.5 🟡 500 au lieu de 401 sur session expirée +### 2.5 ✅ 500 au lieu de 401 sur session expirée — corrigé (2026-07-09) `Authenticate` écrit 401 puis `raise ESessionRejected` → le `try/except` global de `PM.HTTPServer` réécrit un **500**. Pré-existant, tous les diff --git a/delphi-backend/Source/PM.HTTPServer.pas b/delphi-backend/Source/PM.HTTPServer.pas index b119f19..7e39a7b 100644 --- a/delphi-backend/Source/PM.HTTPServer.pas +++ b/delphi-backend/Source/PM.HTTPServer.pas @@ -15,7 +15,7 @@ uses Winapi.Windows, IdHTTPServer, IdContext, IdCustomHTTPServer, IdSocketHandle, IdTCPConnection, PM.Router, PM.JSON, PM.Database, PM.StaticFiles, PM.EmbeddedAssets, - PM.Crypto, PM.ProcessLockdown; + PM.Crypto, PM.ProcessLockdown, PM.Session; type TLogProc = reference to procedure(const AMsg: string); @@ -284,6 +284,9 @@ begin if Assigned(StaticServer) and StaticServer.TryServe(ARequest, AResponse) then Exit; TJSONHelper.SendError(AResponse, 404, 'Not found'); except + // Authenticate/RequireCSRF already wrote the 401 — don't overwrite it + // with a 500. Any handler that doesn't wrap Authenticate lands here. + on ESessionRejected do Exit; on E: Exception do begin Log('ERROR ' + ARequest.Command + ' ' + ARequest.Document + ' : ' + E.Message); @@ -308,6 +311,7 @@ begin if not Router.DispatchRequest(ARequest, AResponse) then TJSONHelper.SendError(AResponse, 404, 'Not found'); except + on ESessionRejected do Exit; // 401 already sent — keep it, don't 500 on E: Exception do begin Log('ERROR ' + ARequest.Command + ' ' + ARequest.Document + ' : ' + E.Message);