PERF-002, PERF-003, PERF-004. Aucun changement de comportement : chaque
reecriture est accompagnee de tests qui enoncent la reponse attendue, pas
la methode.
PERF-002 -- /matches/api/events
Le flux parcourait `tryout.matches` pour chaque tryout visible -- pour un
president, tout l historique du club -- puis posait une requete
MatchParticipant PAR match pour savoir si la personne qui regarde y figure.
Le cout du calendrier croissait avec l historique, a chaque navigation.
FullCalendar envoie deja `start` et `end` sur une source d evenements de
type URL. Personne ne les lisait. La requete est desormais bornee, et les
participants de tous les matchs de la fenetre sont charges en une fois,
joueur compris. Des bornes illisibles sont ignorees plutot que refusees :
un calendrier qui en montre trop est un probleme de performance, un
calendrier qui renvoie 400 est une page blanche.
PERF-003 -- get_players_available_at_time
Chargeait tous les joueurs actifs, puis une requete PlayerDisponibility par
joueur, sur une colonne non indexee. Deux requetes desormais, quelle que
soit la taille du club. Mesure dans le test : 7 requetes pour 6 joueurs
avant, 2 apres.
PERF-004 -- decompte des evaluations en attente
Chargeait toutes les inscriptions du club et toutes les evaluations du
coach, construisait deux ensembles Python et les soustrayait -- deux
lectures de table entiere pour produire un entier. Un COUNT DISTINCT avec
NOT EXISTS.
Les tests couvrent ce que la reecriture aurait pu changer sans bruit : fin
de creneau exclusive, compte desactive exclu, joueur a cheval sur deux
creneaux compte une fois, evaluation d un autre coach qui ne libere pas la
ligne, double inscription comptee une fois (DB-006 n a pas encore atterri,
donc le cas existe).
PERF-001 (view_tryout) n est pas fait : c est le plus gros des quatre, il
touche la page la plus consultee et merite son propre lot.
23 tests ajoutes, 371 au total.
Co-Authored-By: Claude Opus 5 <[email protected]>
QUA-002, premiere moitie. **Ce commit ne fait que reformater** : aucun
changement de comportement, aucune ligne de logique touchee. 72 fichiers,
4 restaient deja conformes. Il est isole exprès, pour que `git log -p` sur
les commits voisins reste lisible.
`quote-style = "preserve"` etait deja pose dans pyproject.toml, ce qui
evite le brassage guillemets simples / doubles : le diff porte sur les
retours a la ligne, l indentation des appels longs et les virgules
finales, pas sur le style de chaine.
Verification : 263 tests passent avant et apres, ruff check propre.
L activation en CI arrive dans le commit suivant, separement, pour que ce
diff-ci ne contienne rien d autre.
Co-Authored-By: Claude Opus 5 <[email protected]>
SEC-XSS-001. Chaine complete : un nom d'utilisateur libre arrivait dans le
DOM d'un coach ou d'un administrateur, en meme origine, avec sa session.
La CSP autorisant 'unsafe-inline', rien ne l'arretait.
Cote serveur - la cause
/matches/api/events construisait de la presentation dans un champ JSON :
match_desc = participants_str + f"<br>{match.description}"
Le navigateur deposait cette valeur telle quelle dans innerHTML. Les noms
de joueurs y transitaient sans echappement -- et il ne pouvait pas y en
avoir : c'est du JSON, pas du HTML.
Les deux valeurs etaient deja des cles distinctes du payload. La
concatenation faisait donc aussi afficher les participants deux fois dans
le modal : une fois dans "Teams", une fois en tete de "Description".
Corriger la faille corrige l'affichage.
Cote navigateur - le sink
showEventModal assemblait une chaine HTML puis l'affectait a innerHTML.
Remplace par une construction de noeuds : makeEl / detailItem /
multilineNode / teamNode passent tout texte par textContent. Les retours
a la ligne d'une description restent rendus, via des <br> crees en dur.
Les deux listes deroulantes concatenaient egalement titres de tryout et
noms d'equipe dans innerHTML. Remplacees par new Option(), dont le
premier argument est pose en texte.
Verification
5 tests sur le contrat de l'API, dont un avec un nom d'utilisateur
hostile ecrit directement en base -- ce que la validation refuse
desormais, mais que des lignes anterieures peuvent contenir.
Le JavaScript inline extrait passe `node --check`.
Reste ouvert : la CSP autorise toujours 'unsafe-inline' (SEC-WEB-001), donc
la defense en profondeur manque encore. Suivi en OPS-010.
Co-Authored-By: Claude Opus 5 <[email protected]>