Commit Graph
97 Commits
Author SHA1 Message Date
GGThedandClaude Opus 5 7bf428f9a0 fix(ops): sauvegarder reellement la base PostgreSQL
DATA-002 / OPS-001. backup.py ciblait SQLite : import sqlite3, DATABASE_PATH
par defaut instance/team_tryouts.db, et l'API de sauvegarde sqlite3. La
production tourne sur PostgreSQL, donc le fichier n'existait pas. Le script
affichait "[WARNING] Database not found... Skipping database backup" -- puis,
main() ne suivant que le resultat de la verification, **sortait avec le code
0**. Toute tache planifiee surveillant le code de sortie voyait vert alors
qu'aucune sauvegarde n'avait jamais ete produite.

Il n'existait donc aucune sauvegarde applicative de la base.

Reecriture
  pg_dump en --format=custom : compresse, et pg_restore permet une
  restauration selective, ce qu'un dump SQL a plat ne permet pas.
  parse_database_url accepte les suffixes de dialecte SQLAlchemy
  (postgresql+psycopg://) que pg_dump ne comprend pas, et refuse
  explicitement une URL SQLite -- le cas exact qui passait en silence.

  Le mot de passe ne figure jamais dans la ligne de commande : il serait
  visible de tout processus capable de lister argv. Il passe par PGPASSWORD.
  Il est egalement absent des messages affiches, qui atterrissent dans les
  journaux du planificateur.

  verify_backup lit l'archive avec pg_restore --list et exige au moins une
  table : une archive illisible ne se restaure pas, et une archive sans
  table signifie que le dump a vise la mauvaise cible. Les deux sont des
  echecs silencieux qu'il vaut mieux attraper maintenant que pendant un
  incident.

  Le code de sortie vaut 0 uniquement si le dump a ete produit ET verifie.

L'archive des documents est conservee : les contrats signes n'existent que
sur disque, la base ne stocke que des chemins. Restaurer l'une sans l'autre
laisse des lignes pointant vers des fichiers absents.

docs/database-restore.md
  Procedure de restauration testable sur une base jetable, requetes de
  controle, demarrage de l'application sur la copie restauree, plan de
  reprise par scenario. ENABLE_DISCORD_BOT=false y est signale comme non
  optionnel : sans lui, l'exercice demarre un vrai bot et envoie de vraies
  notifications a de vraies personnes, a partir de donnees restaurees.

  Les points ouverts sont listes tels quels : aucune copie hors site, pas de
  chiffrement au repos, aucune planification, et l'exercice de restauration
  n'a jamais ete effectue.

17 tests sur ce qui est verifiable sans serveur PostgreSQL : analyse de
l'URL, construction de la commande, non-fuite du mot de passe, et surtout
codes de sortie -- le silence ne vaut plus succes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 20:06:51 -04:00
GGThedandClaude Opus 5 ca45be80db fix(security): proteger le dernier administrateur, et fermer le CORS permissif
SEC-AUTHZ-007 - auto-verrouillage de l'administration
  Le changement de role n'excluait ni l'utilisateur courant, ni le dernier
  compte admin actif. Une seule manipulation suffisait a transformer le seul
  president en joueur, et plus aucune interface ne permettait de revenir en
  arriere : il fallait une intervention directe en base.

  Deux gardes distinctes, parce que ce sont deux erreurs differentes :
    - changer son propre role est refuse, meme s'il reste d'autres admins.
      Un president qui veut se retrograder doit le faire faire par un autre.
    - retrograder le dernier admin actif est refuse.
  Le decompte exclut les comptes desactives : trois admins dont deux
  desactives, cela fait un seul administrateur reel.

SEC-WEB-003 - CORS ouvert par defaut
  Sans CORS_ALLOWED_ORIGINS, la branche else appelait
  CORS(app, supports_credentials=True) sans argument origins. flask-cors
  retient alors '*' et, les identifiants etant autorises, renvoie en echo
  l'Origin de l'appelant avec Access-Control-Allow-Credentials: true --
  l'inverse exact de ce qu'annonçait le commentaire.

  L'exploitation etait bloquee par SESSION_COOKIE_SAMESITE = 'Lax', qui
  empeche le navigateur de joindre le cookie de session a une requete
  fetch inter-site. Toute la protection tenait donc a ce seul reglage.
  Cette application rend du HTML en meme origine : elle n'a besoin
  d'aucune politique CORS. La branche par defaut est supprimee, la
  configuration explicite reste possible.

5 tests ajoutes, dont deux verifient que les chemins legitimes continuent
de fonctionner : un autre administrateur reste retrogradable, et une
origine explicitement configuree est toujours honoree.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 20:03:50 -04:00
GGThedandClaude Opus 5 ab44258d72 feat(obs): journaliser les evenements d'authentification
OBS-001. logging_config.py configurait un fichier auth.log avec rotation,
un logger nomme 'team_tryouts.auth' et un filtre de redaction. Mais
get_auth_logger n'etait importe nulle part : le fichier etait cree et
restait vide. Aucune connexion, aucun echec, aucun verrouillage, aucun
changement de role, aucune suppression de compte ne laissait de trace.
En cas de suspicion de compromission, il n'y avait rien a consulter.

Ajout de log_auth_event(event, **fields), qui emet des paires cle=valeur
ordonnees -- greppable sans dependance de journalisation JSON.

Evenements couverts
  authentification  login.success, login.failure,
                    login.failure.unknown_user, login.rejected.locked,
                    login.rejected.deactivated, account.locked, logout,
                    account.registered
  administration    account.created_by_admin, account.updated,
                    account.role_changed (avec ancien et nouveau role),
                    account.deleted, account.password_reset_by_admin
  libre-service     account.password_changed

Le champ ip vient de request.remote_addr, donc de X-Forwarded-For. Tant que
Waitress tourne avec trusted_proxy='*' (SEC-WEB-002), cette valeur est
choisie par l'appelant : c'est une indication, pas une preuve. Le point est
documente dans la docstring.

Un test a fait remonter ARCH-003, jusqu'ici classe comme fragilite latente
  Journaliser en fin de edit_user levait DetachedInstanceError : le
  changement de role appelle db.session.remove() en plein cycle de requete,
  ce qui detache current_user de la session. Le code s'en tirait parce qu'il
  redirigeait immediatement sans plus y toucher. L'identite de l'acteur est
  desormais capturee en debut de traitement. Le constat est donc confirme
  comme reel, et non plus seulement probable -- sa correction de fond reste
  au programme.

15 tests, dont 4 sur le filtre de redaction lui-meme : c'est un controle de
securite, il doit etre verifie.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 20:01:24 -04:00
GGThedandClaude Opus 5 983b7a1f49 fix(auth): ajouter le parametre state au flux OAuth2 Discord
SEC-AUTH-004. L'URL d'autorisation ne portait que client_id, redirect_uri,
response_type et scope. Sans state, le callback acceptait n'importe quel
code d'autorisation qu'on lui presentait.

Scenario ferme : un attaquant obtient un code pour SON compte Discord, puis
fait charger l'URL de callback par le navigateur de la victime. Le
formulaire d'inscription de la victime se retrouve pre-rempli avec
l'identite Discord de l'attaquant. C'est le login CSRF decrit par la
RFC 6749 §10.12.

Mise en oeuvre
  secrets.token_urlsafe(32) genere le jeton, stocke en session avant la
  redirection. Le callback le compare en temps constant avec
  secrets.compare_digest, et le consomme systematiquement -- valide ou non --
  pour qu'il ne puisse pas etre rejoue. Le controle intervient avant
  l'echange du code : un callback rejete ne declenche aucun appel reseau.

Deux corrections accessoires sur le meme chemin
  - DISCORD_REDIRECT_URI est desormais verifie au meme titre que
    DISCORD_CLIENT_ID. Non defini, il faisait lever requests.utils.quote(None)
    au lieu de signaler un probleme de configuration.
  - la construction de la chaine de requete passe a urlencode() plutot qu'a
    une concatenation manuelle.

9 tests : presence du state, stockage en session, unicite entre deux
demandes, rejet sans state, avec un state forge, sans demande prealable,
et non-rejouabilite.

Reste ouvert : l'identite Discord obtenue reste ensuite reinjectee par un
champ cache du formulaire (SEC-AUTH-005). Le state protege la liaison, pas
encore la valeur elle-meme.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 20:01:24 -04:00
GGThedandClaude Opus 5 d05e9cde32 fix(security): supprimer le XSS stocke du calendrier
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]>
2026-08-07 19:56:27 -04:00
GGThedandClaude Opus 5 32f193c008 style: f-strings sans placeholder signalees par ruff (F541)
Aucun changement de comportement. Les chaines concernees ne contenaient
aucune substitution.

A noter pour plus tard : run_https.py conserve une banniere en caracteres
semi-graphiques, du meme type que celle qui faisait planter security_scan.py
sur une console Windows en cp1252. Le script n'etant lance qu'en
developpement et de facon explicite, le point est signale sans etre corrige
ici.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:47:45 -04:00
GGThedandClaude Opus 5 5ecea55f55 fix(ci): rendre la chaine d'integration reellement verifiante
Les quatre jobs existaient ; aucun ne verifiait ce qu'il annoncait.

security-audit
  `pip-audit --require-hashes --no-deps || pip-audit`. L'etape
  d'installation ne posait que pip-audit, et aucune des deux formes ne
  nommait le fichier d'exigences : le repli auditait l'environnement du
  runner, qui ne contenait que pip-audit lui-meme. Le job passait au vert
  sans avoir examine une seule dependance de l'application. Remplace par
  `pip-audit -r requirements.txt`.

security-scan
  Appelait `python security_scan.py`, alors que le fichier se trouve dans
  app/supporting_scripts/. En echec a chaque execution depuis le
  deplacement du fichier. Trois autres defauts sont apparus en le faisant
  tourner :
    - la CI passe --skip-http, un argument que l'argparse du script
      n'acceptait pas : sortie en erreur 2 meme avec le bon chemin.
    - check_dependencies lisait data['dependencies'] comme la liste des
      vulnerabilites. Ce tableau liste en realite TOUTES les dependances,
      chacune portant un champ vulns vide si le paquet est sain. Les ~45
      paquets installes etaient donc signales vulnerables a chaque
      execution. Le filtrage se fait desormais sur vulns non vide.
    - check_flask_config interceptait son exception et renvoyait quand
      meme all_ok : ne pas reussir a charger l'application comptait comme
      un controle reussi. La section la plus importante du rapport n'avait
      jamais tourne. Elle renvoie desormais False, et l'import fonctionne
      grace a l'ajout de la racine du projet dans sys.path.
    - la banniere en caracteres semi-graphiques faisait planter le script
      sur une console Windows en cp1252, la plateforme meme du projet.
      Passee en ASCII.

lint
  Ruff n'avait aucun fichier de configuration : le job tournait sur le jeu
  de regles par defaut. La configuration vit maintenant dans pyproject.toml.
  `ruff format --check` est retire pour l'instant : la base n'ayant jamais
  ete formatee, il echouerait sur 62 fichiers sur 64 pour des raisons
  etrangeres a la correction. Reformatage puis application : QUA-002.

test
  Un `echo` protege par continue-on-error : le job annoncait un succes
  sans rien executer. Il lance desormais pytest avec couverture, et bloque.

permissions: contents: read au niveau du workflow, aucune etape n'ecrivant
dans le depot.

Deploiement Gitea
  actions/checkout@v7 n'existe pas (derniere majeure : v5) : le workflow
  echouait des sa premiere etape. Ramene a v4.
  Le miroir lftp poussait l'integralite de l'arbre de travail, dont
  clear_db.py -- un script qui vide toutes les tables et recree
  admin/password -- vers le noeud de production. Liste d'exclusions ajoutee.
  --delete reste volontairement absent : les contrats televerses, les
  journaux et le .env du serveur vivent sous la racine de deploiement et
  sont absents du depot ; les supprimer detruirait des donnees.

Le workflow de deploiement n'a pas pu etre execute depuis ici : la
syntaxe lftp reste a valider lors du prochain deploiement manuel.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:47:24 -04:00
GGThedandClaude Opus 5 a793b7ed0d fix(authz): verifier le rattachement equipe/tryout, et nettoyer le code mort
SEC-AUTHZ-002. add_to_team recevait tryout_id et team_id independamment
dans l'URL, controlait l'autorisation sur le tryout, puis operait sur
l'equipe sans jamais etablir de lien entre les deux. Un gestionnaire du
tryout A pouvait donc modifier une equipe du tryout B.

Le lint pointait exactement dessus : `team` etait charge ligne 463 puis
jamais utilise. La correction automatique proposee etait de supprimer la
variable, ce qui aurait fait taire l'avertissement en cimentant la faille.
Elle est desormais utilisee pour ce a quoi elle servait.

Trois defauts sur la meme route, corriges ensemble :
  - team.tryout_id != tryout_id repond maintenant 404
  - seuls les joueurs inscrits au tryout peuvent rejoindre ses equipes
  - int(player_id) sur une entree de formulaire brute levait ValueError,
    donc une erreur 500, sur toute valeur non numerique

Nettoyage automatique par ruff : 34 imports et variables morts retires
sur l'ensemble du paquet. La suite de tests a servi de filet, elle passe
a l'identique avant et apres. Aucun changement de comportement.

A noter, OneOnOneRequestSchema figurait aussi parmi les imports morts :
c'est un quatrieme schema jamais appele, la route one_on_one validant ses
dates a la main. Unifier la validation reste a faire (ARCH-005).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:47:00 -04:00
GGThedandClaude Opus 5 b277c453f9 fix(security): appliquer les schemas de validation sur la gestion des comptes
SEC-AUTHZ-001. CreateUserSchema, EditUserSchema et EditProfileSchema
etaient importes dans users.py et jamais appeles : chaque nom
n'apparaissait qu'une fois dans le fichier, sur sa ligne d'import. Les
trois routes lisaient request.form directement.

Consequences levees :
  - aucune politique de mot de passe sur create_user, edit_user et
    edit_profile. Un mot de passe d'un caractere etait accepte pour un
    compte administrateur.
  - aucune validation de format sur username, email, phone,
    discord_user_id.
  - edit_user ne verifiait pas l'unicite du courriel avant affectation :
    la contrainte unique remontait en IntegrityError, donc en erreur 500.
    Un controle explicite excluant l'utilisateur courant est ajoute.

C'est aussi le point d'injection de la chaine de XSS stocke SEC-XSS-001 :
edit_profile acceptait n'importe quel nom d'utilisateur, charge HTML
comprise, qui ressortait ensuite en JSON via /matches/api/events et
etait injectee par innerHTML dans le calendrier.

Deux details de formulaire imposaient un adaptateur, _form_payload :
  - request.form.to_dict() ne conserve que la premiere valeur d'une cle
    repetee, donc games doit etre relu avec getlist().
  - une case a cocher non cochee est absente de la soumission, ce qui
    n'est pas la meme chose qu'un load_default. Sans injection explicite,
    decocher is_active_account aurait cesse de desactiver le compte.
  - un mot de passe vide signifie "conserver l'actuel" et non "definir le
    mot de passe vide" : le champ est retire avant validation.

Le controle manuel du role devient redondant, le schema le contraint deja
par OneOf(USER_TYPES).

Verifie par quatre tests qui echouaient avant ce changement.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:47:00 -04:00
GGThedandClaude Opus 5 1b990a84d9 test: socle de tests executables et fabrique d'application parametrable
Il n'existait aucun test, et le code n'offrait aucune prise pour en ecrire :
create_app() exigeait SECRET_KEY et DATABASE_URL dans l'environnement,
creait les tables et demarrait un bot Discord. C'etait la cause, pas le
symptome.

create_app(config=None)
  Les valeurs par defaut viennent toujours de l'environnement, les
  surcharges de l'appelant sont appliquees ensuite, et la validation
  vient en dernier pour qu'un test puisse fournir les siennes. Deux
  effets de bord passent sous drapeau, actifs par defaut pour que la
  production et le developpement se comportent a l'identique :
    AUTO_CREATE_TABLES   controle db.create_all()
    ENABLE_DISCORD_BOT   controle start_bot()
  FORCE_HTTPS passe egalement en configuration : lu via os.getenv a
  chaque requete, il renvoyait un 301 sur tout appel de test.

Suite de tests : 47 tests, 3 xfail, 32 % de couverture.
  tests/conftest.py            fabriques par role, connexion par le vrai
                               formulaire, base SQLite temporaire
  test_auth_session.py         expiration de session, desactivation de
                               compte, deconnexion
  test_security_headers.py     en-tetes, non-divulgation sur /health,
                               echappement de nl2br
  test_authorization.py        acces anonyme, vertical, horizontal,
                               validation des entrees, CSRF

Les tests marques xfail(strict=True) decrivent des constats non encore
corriges. Ils echouent par construction ; le mode strict transforme une
reussite inattendue en echec, ce qui signale qu'il faut retirer le
marqueur. Trois subsistent : enumeration de comptes (SEC-AUTH-006), CSP
unsafe-inline (SEC-WEB-001), auto-retrogradation du dernier administrateur
(SEC-AUTHZ-007).

pyproject.toml
  Configuration pytest et ruff. Ruff n'avait aucune configuration : la CI
  l'executait avec le jeu de regles par defaut. Les 33 F401 de
  app/models/__init__.py sont ignores par fichier, c'est une facade de
  re-export intentionnelle.

requirements-dev.txt separe l'outillage de test des dependances de
production.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:46:34 -04:00
GGThedandClaude Opus 5 de9448a9aa fix(security,obs): fuite d'erreur sur /health, filtre nl2br, redaction des logs
/health divulguait le message brut du pilote
  L'endpoint n'est pas authentifie et renvoyait f'error: {str(e)}'. Les
  exceptions psycopg contiennent regulierement l'hote, le port, le nom de
  la base et l'utilisateur. Le detail part desormais dans les journaux,
  la reponse ne porte plus qu'un statut.

Filtre nl2br non echappant
  Markup('<br>'.join(...)) marquait le texte comme sur sans l'echapper.
  Le filtre n'etant utilise dans aucun gabarit, la faille etait latente :
  elle se serait ouverte au premier usage. Corrige en Markup('<br>').join(),
  qui echappe chaque segment. Verifie : nl2br('<script>alert(1)</script>')
  rend desormais &lt;script&gt;alert(1)&lt;/script&gt;.

Filtre de redaction des secrets sans effet
  SensitiveDataFilter n'inspectait que record.msg. Or le code journalise
  en style parametre ('...: %s', valeur) : record.msg ne contient que la
  chaine de format, et la donnee sensible vit dans record.args, ignore.
  La redaction ne s'appliquait donc pratiquement jamais. Le record est
  desormais rendu avant filtrage, puis args vide.

Sortie console conditionnee a FLASK_DEBUG
  En production, l'application n'ecrivait rien sur stdout, precisement ou
  regarde la console Pterodactyl. Le handler devient inconditionnel, seul
  son niveau varie.

Journaux du bot Discord perdus
  discord_bot.py utilise getLogger(__name__), soit 'app.discord_bot'.
  Aucun handler n'etait attache a la hierarchie 'app' : les INFO etaient
  jetes et les WARNING+ tombaient sur le handler de dernier recours, sans
  format. Les handlers sont desormais rattaches au logger de paquet.

X-XSS-Protection retire (app.py et nginx.conf)
  En-tete deprecie, l'auditeur vise a ete supprime des navigateurs
  courants et ses dernieres implementations introduisaient elles-memes
  des vulnerabilites.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:16:37 -04:00
GGThedandClaude Opus 5 2c37c05c8f fix(auth): rendre effectives l'expiration de session et la desactivation de compte
Deux protections etaient configurees sans avoir d'effet.

Expiration de session
  app.py:73 definit PERMANENT_SESSION_LIFETIME = 3600, mais Flask
  n'applique cette duree qu'aux sessions marquees permanentes. Aucune
  occurrence de session.permanent n'existait dans app/. Le cookie emis
  etait donc un cookie de session navigateur, sans expiration, et le
  serveur ne verifiait aucune anciennete. Ajout de session.permanent
  juste avant login_user, apres la rotation anti-fixation.

Desactivation de compte
  is_active_account n'etait consulte qu'au moment du login (auth.py:141).
  User n'ayant pas surcharge is_active, UserMixin renvoyait True en
  permanence. Desactiver un compte empechait donc la reconnexion mais
  laissait vivre la session en cours.

  La propriete is_active seule ne suffit pas : Flask-Login ne la consulte
  qu'a l'appel de login_user, jamais lors de la restauration d'une session
  depuis le cookie. Le verrou effectif est donc dans load_user, qui renvoie
  desormais None pour un compte desactive. La propriete est ajoutee malgre
  tout pour que login_user soit coherent avec le chargeur.

load_user passe au passage de Query.get() (API heritee, avertie en
SQLAlchemy 2.0) a db.session.get(), et tolere un identifiant non entier
sans lever.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:16:37 -04:00
GGThedandClaude Opus 5 56aee7f3f3 fix(deps): rendre requirements.txt installable sur Python 3.12
Trois problemes distincts empechaient une installation propre.

1. audioop-lts==0.2.2 declare Requires-Python >=3.13. Le fichier ayant
   ete produit par un pip freeze sur 3.13, la ligne etait inconditionnelle
   et faisait echouer `pip install -r requirements.txt` sur le 3.12 cible
   par la CI. Les jobs security-scan et test n'atteignaient donc jamais
   leur etape utile. Ajout du marqueur python_version >= "3.13" : le
   backport n'est tire que la ou audioop a quitte la bibliotheque standard.

2. Fichier encode en UTF-16 LE. pip sait le lire grace au BOM, mais
   l'outillage tiers non, et le diff est illisible. Reecrit en UTF-8, LF.

3. psycopg[binary] etait la seule dependance non epinglee. Epinglee a la
   version effectivement resolue (3.2.12).

Retrait de trois dependances parasites, verifiees non importees sur
l'ensemble de app/ et de la racine :
  - dotenv==0.9.9    doublon relais de python-dotenv (seul import reel)
  - discord==2.3.2   doublon relais de discord.py (seul import reel)
  - login==0.0.6     sans rapport avec Flask-Login, jamais reference

WTForms est conserve : c'est une dependance transitive legitime de
Flask-WTF (utilisee par CSRFProtect), et non une dependance directe morte.

Installation verifiee en environnement neuf sous Python 3.12.10.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 19:16:12 -04:00
GGThedandClaude Opus 5 90866f830e chore(gitignore): ignorer les artefacts d assistants IA et corriger la regle *.html
- Ajout des dossiers/fichiers des assistants IA (Claude, ChatGPT/OpenAI,
  Cursor, Copilot, Aider, Windsurf, Gemini, Continue, Cline).
- Ajout des notes de travail et de suivi generees par assistant (.ai/,
  audit/), retirees du suivi git via `git rm --cached` : les fichiers
  restent sur disque mais ne sont plus versionnes.
- Retrait des regles `docs/` et `*.html`, qui ignoraient TOUT fichier
  .html du depot, y compris les templates Jinja2. Un nouveau template
  etait invisible pour git : fonctionnel en local, TemplateNotFound en
  production, sans signal dans `git status`. Les rapports de couverture
  sont desormais couverts par htmlcov/ et coverage_html_report/.
- `.env` -> `.env*` avec exception `!.env.example`, pour couvrir
  .env.local, .env.production et les copies de sauvegarde.
- Ajout des cles et certificats, des sauvegardes (backups/), des
  journaux (logs/), des environnements virtuels, des caches d outils
  et des fichiers d editeur/OS.
- Deduplication des regles existantes (*.db et instance/ etaient en
  double, .instance/ n existait pas).

Aucune modification du code applicatif.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 13:27:13 -04:00
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
GGThedandClaude Opus 5 fa0a378827 docs(audit): audit securite, maintenabilite et standards de la stack
Revue statique de l'ensemble du code Python, de la configuration CI/nginx,
du .gitignore et des dependances. 44 constats documentes avec references
fichier:ligne, impact et correctif propose.

- audit/01-securite.md         19 constats (4 critiques)
- audit/02-maintenabilite.md   15 constats
- audit/03-standards-stack.md  10 ecarts aux conventions Flask/SQLAlchemy
- audit/plan-remediation.md    ordre de traitement en 6 lots

Points critiques : secrets de production reels committes dans
app/.env.exemple, seed automatique en production avec mot de passe
password, CORS ouvert a toutes les origines avec credentials par
defaut, token du bot Discord imprime sur stdout au demarrage.

Aucune modification du code applicatif.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-07 13:02:54 -04:00
cedrick2711 bb0bc1c192 ajout de plateforme de base pour les url TRN 2026-08-06 20:13:39 -04:00
cedrick2711 f89e4deb30 régler problème avec mise a jour des status de message du discord bot 2026-08-06 15:26:00 -04:00
cedrick2711 fcf10bcdff bug fix:
Manager ne pouvait pas voir les tryouts.
probleme avec discord bot
2026-08-06 14:46:51 -04:00
cedrick2711 68e3da6601 fix probleme avec dispos 2026-08-06 13:41:53 -04:00
cedrick2711 d25b35c928 régler erreur 500 sur changement de role par admin 2026-08-04 22:06:43 -04:00
cedrick2711 aeba4d78cf régler problème de changement de rôle 2026-08-04 21:52:52 -04:00
cedrick2711 7bb80229db added a clear_db to start fresh with only an admin 2026-08-04 13:09:19 -04:00
cedrick2711 0dd4ecdb4c Erreur de frappe dans un des dossiers 2026-08-04 12:57:18 -04:00
cedrick2711 0f7788e973 Changement du layout de la page d'enregistrement 2026-08-04 12:35:25 -04:00
cedrick2711 47d5ec47e7 added discord oauth2 to get basic user info to complete profile when registering 2026-08-04 12:25:03 -04:00
cedrick2711 10af8c0d22 small change for prod 2026-08-03 23:08:44 -04:00
cedrick2711 7d30aff986 Corriger erreur d'enregistrement 2026-08-03 21:51:23 -04:00
m3ow 409741642a Update wsgi.py 2026-08-03 16:43:46 -04:00
m3ow 056cea0635 Update .gitea/workflows/git-to-ptero.yaml 2026-08-02 19:46:16 -04:00
m3ow d3b5700977 Add .gitea/workflows/git-to-ptero.yaml 2026-08-02 18:39:08 -04:00
clubesportsudes fd258de8c9 Update app/.env.exemple
CI - Security & Lint / Security Audit (push) Has been cancelled
CI - Security & Lint / Lint with Ruff (push) Has been cancelled
CI - Security & Lint / Security Scan (push) Has been cancelled
CI - Security & Lint / Tests (push) Has been cancelled
2026-08-02 18:36:54 -04:00
cedrick2711 d6fe505628 Merge branch 'main' of https://github.com/cedrick2711/team-tryouts 2026-07-30 13:25:05 -04:00
cedrick2711 35c07c263d Merge branch 'dev' of https://github.com/cedrick2711/team-tryouts 2026-07-30 13:25:02 -04:00
cedrick2711 251060ce58 Merge branch 'dev' of https://github.com/cedrick2711/team-tryouts into dev 2026-07-30 13:24:46 -04:00
cedrick2711 51e6b81ac7 régler problème ou les coachs ne voyait pas leur tryouts 2026-07-30 13:24:43 -04:00
cedrick2711 7c375f45dd mise à jour du README 2026-07-30 09:52:32 -04:00
cedrick2711 2d3721b201 Ajout d'un .env.exemple pour simplifier la collaboration 2026-07-30 08:07:28 -04:00
cedrick2711 e629067434 Merge branch 'dev' of https://github.com/cedrick2711/team-tryouts 2026-07-29 20:25:01 -04:00
cedrick2711 19c6740edb régler problème avec le bot discord et ajouter un panneau pour gérer les one on one (accepter ,refuser, confirmer) 2026-07-29 20:24:10 -04:00
cedrick2711 962e621fee régler problème ou on ne voyait pas les dates pour book un One on One 2026-07-29 17:17:11 -04:00
cedrick2711 5e7d7324de Merge branch 'dev' of https://github.com/cedrick2711/team-tryouts 2026-07-29 17:01:41 -04:00
cedrick2711 ad7b932c0a régler les problèmes de route 2026-07-29 17:01:28 -04:00
cedrick2711 da38f90641 Merge branch 'dev' of https://github.com/cedrick2711/team-tryouts 2026-07-29 15:59:45 -04:00
cedrick2711 1702139250 corriger erreur de nom 2026-07-29 15:58:40 -04:00
cedrick2711 b02316ed7c Merge branch 'dev' of https://github.com/cedrick2711/team-tryouts 2026-07-29 15:55:36 -04:00
cedrick2711 3bfe9a99a5 ajustement pour la nouvelle db 2026-07-29 15:53:29 -04:00
cedrick2711 a5bdbefa89 rajout de db dans gitignore 2026-07-29 14:58:14 -04:00
cedrick2711 80ff12f51a rajout de la db dans gitignore 2026-07-29 14:57:01 -04:00
cedrick2711 1337d8e244 delete requirement 2026-07-29 14:49:01 -04:00