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]>
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(commitfd258de) 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 PostgreSQL — psycopg[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.auth — aprè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_addrtant quetrusted_proxy='*'produit une traçabilité illusoire — pire que pas de traçabilité, parce qu'on lui fera confiance.