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>
25 lines
882 B
Python
25 lines
882 B
Python
"""Jeton CSRF — un seul jeton par SESSION (pas un par formulaire, comme le
|
|
ferait Flask-WTF) : suffisant ici et beaucoup plus simple à propager
|
|
partout où l'appli envoie une requête qui modifie quelque chose, que ce
|
|
soit un <form> classique (intercepté par pjax.js, voir static/pjax.js) ou
|
|
un fetch() construit à la main (screen_edit.html, game_dashboard.html,
|
|
play.html...) — voir static/csrf_fetch.js, qui l'ajoute automatiquement en
|
|
en-tête à CHAQUE fetch() non-GET de l'appli plutôt que de devoir modifier
|
|
individuellement chacun des nombreux appels existants."""
|
|
import secrets
|
|
|
|
from flask import session
|
|
|
|
from .flask_app import app
|
|
|
|
|
|
def get_csrf_token():
|
|
token = session.get("csrf_token")
|
|
if not token:
|
|
token = secrets.token_urlsafe(32)
|
|
session["csrf_token"] = token
|
|
return token
|
|
|
|
|
|
app.jinja_env.globals["csrf_token"] = get_csrf_token
|