Files
team-tryouts/audit/plan-remediation.md
T
GGThedandClaude Opus 5 ed586233f6 docs(audit): rebase sur le depot de reference et reverification complete
Le miroir GitHub audite en premiere passe etait en retard de 16 commits
sur git.immortal.host/clubesportsudes/team-tryouts. L'audit est rebase
sur immortal/main @ bb0bc1c et l'ensemble des constats reverifie.

Resolus par l'equipe (archives dans 00-provenance.md) :
- seed automatique en production supprime
- print du token Discord supprime
- proxy_pass nginx corrige vers 127.0.0.1
- dossier supporting_scrits renomme

Nouveaux constats :
- SEC-20 flux OAuth2 Discord sans parametre state (CSRF de liaison)
- SEC-21 le deploiement SFTP pousse .git/ sur le serveur
- SEC-22 clear_db.py destructif sans garde-fou, admin/password en dur
- MNT-16 discord_pending.json versionne

Requalifies :
- SEC-01 secrets retires du fichier mais toujours dans l'historique des
  deux depots, et dans le HEAD du miroir GitHub -> revocation requise
- SEC-05 trusted_proxy='*' + bind 0.0.0.0 rend X-Forwarded-For usurpable,
  ce qui ouvre le rate limiting au lieu de le corriger
- SEC-07 l'OAuth2 ajoute ne contraint pas l'identite : le discord_user_id
  transite par un champ cache du formulaire
- MNT-05 psycopg[binary] non epingle, incompatible avec les URI
  postgresql:// que SQLAlchemy resout vers psycopg2

48 constats. Aucune modification du code applicatif.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 13:17:08 -04:00

9.6 KiB

Plan de remédiation

Ordre proposé. Les lots sont séquentiels : chacun lève des blocages du suivant.

Établi sur immortal/main @ bb0bc1c. Les constats déjà résolus par l'équipe sont listés dans 00-provenance.md et absents de ce plan.


Lot 0 — Aujourd'hui, avant tout commit

Ces actions ne sont pas du code.

# Action Réf.
0.1 Révoquer le token du bot Discord (Developer Portal → Bot → Reset Token) SEC-01
0.2 Faire tourner le mot de passe PostgreSQL sur Render SEC-01
0.3 Générer une nouvelle SECRET_KEY — invalide toutes les sessions, c'est voulu SEC-01
0.4 Vérifier en production si les comptes de l'ancien seed (admin, manager1, manager2, coach1, coach2, coach3, scout1) existent avec le mot de passe password ; les désactiver ou changer leur mot de passe SEC-22
0.5 Vérifier si .git/ est présent et téléchargeable sur le serveur de production (curl https://<app>/.git/HEAD) ; le supprimer le cas échéant SEC-21
0.6 Consulter les logs Discord et PostgreSQL pour détecter un éventuel accès non autorisé SEC-01

Le nettoyage de app/.env.exemple (commit fd258de) n'a rien révoqué. Les valeurs restent lisibles dans l'historique des deux dépôts, et dans le HEAD du miroir GitHub. 0.1 à 0.3 restent nécessaires et sont indépendantes de toute manipulation de git.


Lot 1 — Correctifs critiques

# Action Réf. Effort
1.1 Purger l'historique des deux dépôts (git filter-repo --path app/.env.exemple --invert-paths), prévenir l'équipe de recloner SEC-01 2 h
1.2 Synchroniser ou supprimer le miroir GitHub — il expose encore les secrets dans son HEAD SEC-01 30 min
1.3 Ajouter .env* au .gitignore avec !.env.example ; renommer .env.exemple.env.example SEC-01 15 min
1.4 Refuser le démarrage si CORS_ALLOWED_ORIGINS est vide hors développement SEC-03 20 min
1.5 trusted_proxy='127.0.0.1' + HOST par défaut à 127.0.0.1 dans wsgi.py SEC-05 20 min
1.6 Exclure .git/ (et .github/, __pycache__/, logs/) du miroir SFTP ; épingler known_hosts SEC-21 1 h
1.7 Ajouter un scan de secrets (gitleaks) à la CI SEC-01 30 min

Lot 2 — Débloquer l'outillage

À faire tôt : sans CI fiable et sans tests, les lots suivants ne peuvent pas être validés.

# Action Réf. Effort
2.1 Vérifier le pilote PostgreSQLpsycopg[binary] est psycopg 3 ; confirmer que DATABASE_URL utilise postgresql+psycopg://, ou normaliser l'URI au démarrage. Épingler la version. MNT-05 1 h
2.2 Réencoder requirements.txt en UTF-8, ajouter .gitattributes MNT-02 15 min
2.3 Corriger .gitignore (*.html, docs/), vérifier les fichiers déjà perdus MNT-01 30 min
2.4 Corriger le chemin de security_scan.py dans la CI et lui fournir DATABASE_URL MNT-04 20 min
2.5 Ajouter pyproject.toml avec la configuration Ruff, formater en un commit isolé MNT-06 2 h
2.6 Retirer dotenv, login, discord des dépendances MNT-05 30 min
2.7 Retirer discord_pending.json du suivi git MNT-16 10 min
2.8 Créer tests/, avec les tests de la matrice de permissions ; activer réellement le job CI MNT-03 1 j

Lot 3 — Sécurité applicative

# Action Réf. Effort
3.1 Ajouter le paramètre state au flux OAuth2 Discord SEC-20 1 h
3.2 Prendre discord_user_id depuis la session OAuth2, pas depuis le champ caché du formulaire SEC-07 2 h
3.3 Brancher CreateUserSchema, EditUserSchema, EditProfileSchema sur leurs routes SEC-06 3 h
3.4 Garde-fous sur clear_db.py : confirmation, variable d'environnement, mot de passe aléatoire SEC-22 1 h
3.5 Adosser Flask-Limiter à Redis ou PostgreSQL ; réévaluer les seuils (dont register, passé à 20/h) SEC-08 · SEC-13 2 h
3.6 Restreindre view_user et réduire les données transmises au template SEC-15 2 h
3.7 Valider les téléversements de contrats signés SEC-11 1 h
3.8 Uniformiser les messages de login, égaliser les temps de réponse SEC-12 1 h
3.9 Corriger nl2br (échapper avant Markup) ou le supprimer SEC-09 15 min
3.10 Masquer l'erreur brute de /health SEC-14 10 min
3.11 Alimenter le logger team_tryouts.authaprès 1.5 SEC-19 2 h
3.12 Contrôler la cohérence tryout/équipe dans add_to_team SEC-17 30 min
3.13 Remplacer les int() nus par request.form.get(..., type=int) SEC-16 2 h

Lot 4 — Fondations structurelles

Ce lot débloque des correctifs de sécurité qui exigent des changements de schéma.

# Action Réf. Effort
4.1 Introduire Flask-Migrate, flask db stamp head ; reprendre migrations/add_tryout_coaches.py comme première révision Alembic STD-01 3 h
4.2 Objets de configuration par environnement ; un seul load_dotenv() ; vérifier les 3 variables Discord OAuth2 au démarrage STD-05 4 h
4.3 Sortir les effets de bord de create_app() — bénéficie aussi à clear_db.py et aux migrations STD-02 3 h
4.4 Séparer le bot Discord en processus distinct ; persister son état en base STD-03 · MNT-16 1 j
4.5 (dépend de 4.1) Unicité sur discord_user_id SEC-07 2 h
4.6 (dépend de 4.1) Cascades FK sur OrgTeam ; corriger delete_team MNT-11 3 h
4.7 (dépend de 4.1) Migrer vers datetime.now(timezone.utc) et DateTime(timezone=True) MNT-13 4 h
4.8 Journaliser sur stdout ; handlers fichier optionnels STD-07 1 h
4.9 Aligner les ports (wsgi.py défaut 5000, run.py en 127.0.0.1) STD-06 30 min

Lot 5 — Dette de conception

Sans urgence. À traiter au fil des évolutions plutôt qu'en chantier dédié.

# Action Réf.
5.1 Découper users.py (1 265 lignes) en cinq blueprints ; sortir l'OAuth2 de auth.py MNT-08
5.2 Choisir une approche de validation unique ; retirer Flask-WTF ou WTForms STD-04
5.3 Extraire les utilitaires date/heure MNT-12
5.4 Résorber les N+1 sur view_tryout et api_events MNT-10
5.5 Blueprint /api avec ses propres gestionnaires d'erreur STD-09
5.6 Extraire une couche de services STD-08
5.7 Durcir la CSP (nonces, scripts externalisés) SEC-10
5.8 Réinitialisation de mot de passe par courriel ; MFA sur les rôles d'encadrement SEC-18
5.9 Remplacer le CAPTCHA par une validation du courriel institutionnel SEC-13
5.10 Réparer backup.py (pg_dump) ou le restreindre aux documents MNT-07
5.11 Pagination des listes MNT-14
5.12 Dockerfile + docker-compose.yml ; remplacer le miroir SFTP par un artefact STD-10
5.13 Réécrire le README, en indiquant quel dépôt fait autorité MNT-15

Dépendances entre lots

Lot 0 (révocation, vérifications en prod)
   └─→ Lot 1 (correctifs critiques)
          └─→ Lot 2 (outillage : pilote DB, CI verte, premiers tests)
                 ├─→ Lot 3 (sécurité applicative)
                 │      └─ 3.11 journal d'audit    (bloqué par 1.5 — sinon on journalise des IP usurpables)
                 └─→ Lot 4 (migrations, configuration)
                        ├─→ 4.5 unicité discord_user_id   (bloqué par 4.1)
                        ├─→ 4.6 cascades FK                (bloqué par 4.1)
                        └─→ 4.7 datetimes conscients       (bloqué par 4.1)
                               └─→ Lot 5 (dette de conception)

Deux dépendances méritent attention :

  • 2.1 avant tout le reste du Lot 2 : si le pilote PostgreSQL est mal apparié à l'URI, l'application ne démarre pas, et aucun test d'intégration ne peut être écrit.
  • 1.5 avant 3.11 : journaliser request.remote_addr tant que trusted_proxy='*' produit une traçabilité illusoire — pire que pas de traçabilité, parce qu'on lui fera confiance.