Files
Forge-Engine/core/csrf_guard.py
T
williamandClaude Sonnet 5 a52b244f27 Ajoute la protection CSRF sur tous les formulaires et requêtes AJAX
Un jeton unique par session (core/csrf.py, exposé côté Jinja via
csrf_token()) est vérifié sur toute requête non-GET par un before_request
(core/csrf_guard.py), dans le même esprit que core/auth_guard.py : une
seule garde globale plutôt que de toucher aux ~90 routes existantes une
par une.

L'app entière fait déjà transiter ses formulaires par fetch() : pjax.js
intercepte chaque <form> interne et le transforme lui-même en requête
fetch (aucun usage de l'attribut d'échappement data-no-pjax nulle part
dans le repo, confirmé par grep). Il suffit donc de patcher window.fetch
UNE SEULE FOIS (static/csrf_fetch.js) pour y ajouter automatiquement
l'en-tête X-CSRFToken sur toute requête non-GET, formulaires pjax comme
fetch() écrits à la main dans screen_edit.html/game_dashboard.html/
play.html — sans modifier un seul appel existant.

La vérification est désactivée quand app.config["TESTING"] est actif
(même convention que Flask-WTF/WTF_CSRF_ENABLED), pour ne pas avoir à
ajouter le jeton aux ~170 tests existants qui appellent les routes
directement via le client de test Flask. tests/test_csrf.py réactive
volontairement la garde pour la mettre à l'épreuve pour de vrai (GET
jamais bloqué, POST sans jeton/avec mauvais jeton -> 400, POST avec le
bon jeton via l'en-tête ou le champ de formulaire -> succès).

templates/play.html reçoit les mêmes deux balises que base.html car il
est autonome (ne l'étend pas, propre <html>/<head>).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-28 20:35:32 +02:00

40 lines
1.8 KiB
Python

"""Vérifie le jeton CSRF (voir core/csrf.py) sur toute requête qui modifie
quelque chose — un before_request de plus, dans le même esprit que
core/auth_guard.py (une seule garde globale plutôt que de toucher aux
~90 routes existantes une par une).
Toute l'appli soumet ses formulaires ET ses fetch() via une seule voie :
pjax.js intercepte CHAQUE <form> interne et le transforme lui-même en
fetch() (voir static/pjax.js) — aucun formulaire de ce moteur ne fait donc
jamais de vraie soumission HTML native. static/csrf_fetch.js patche
window.fetch une seule fois pour y ajouter automatiquement l'en-tête
X-CSRFToken sur toute requête non-GET de l'appli (celles de pjax.js
comprises) : pas besoin de modifier individuellement les nombreux appels
fetch() déjà écrits à la main dans screen_edit.html/game_dashboard.html/
play.html."""
from flask import abort, request, session
from .flask_app import app
_SAFE_METHODS = {"GET", "HEAD", "OPTIONS"}
@app.before_request
def _verify_csrf_token():
if request.method in _SAFE_METHODS:
return None
if app.config.get("TESTING"):
# Les tests appellent les routes directement via le client de
# test Flask (de vraies requêtes HTTP, mais jamais passées par le
# navigateur/static/csrf_fetch.js qui pose l'en-tête) — même
# convention que Flask-WTF (WTF_CSRF_ENABLED désactivé en test).
# Ce garde a ses PROPRES tests dédiés (test_csrf.py), qui
# réactivent la vérification explicitement pour la mettre à
# l'épreuve.
return None
submitted = request.headers.get("X-CSRFToken") or request.form.get("csrf_token")
expected = session.get("csrf_token")
if not expected or not submitted or submitted != expected:
abort(400, description="Jeton de sécurité manquant ou expiré — recharge la page et réessaie.")
return None