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]>
70 lines
4.8 KiB
Markdown
70 lines
4.8 KiB
Markdown
# Audit — Team Tryouts
|
|
|
|
Audit statique de l'application `team-tryouts` (Flask 3.1 / SQLAlchemy 2.0 / Jinja2 / PostgreSQL).
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Branche** | `audit/securite-maintenabilite-standards` |
|
|
| **Base** | `main` @ `08f02f7` |
|
|
| **Date** | 2026-08-07 |
|
|
| **Périmètre** | Sécurité · Maintenabilité · Respect des standards de la stack |
|
|
| **Méthode** | Revue de code statique (100 % du code Python, config CI/nginx, `.gitignore`, dépendances). Aucun test dynamique, aucune exécution de l'app. |
|
|
|
|
## Documents
|
|
|
|
| Fichier | Contenu |
|
|
|---|---|
|
|
| [`01-securite.md`](01-securite.md) | 19 constats de sécurité, du critique au faible |
|
|
| [`02-maintenabilite.md`](02-maintenabilite.md) | 15 constats de maintenabilité et d'outillage |
|
|
| [`03-standards-stack.md`](03-standards-stack.md) | 10 écarts aux conventions Flask / SQLAlchemy / 12-factor |
|
|
| [`plan-remediation.md`](plan-remediation.md) | Ordre de traitement proposé |
|
|
|
|
---
|
|
|
|
## Synthèse exécutive
|
|
|
|
L'application est fonctionnellement riche et le socle est sain sur plusieurs points : ORM utilisé partout (**aucune injection SQL**), autoescape Jinja2 actif et **aucun `|safe`** dans les 40 templates, CSRF activé globalement, hachage de mots de passe via Werkzeug, modèle d'autorisation polymorphe cohérent (`can_manage_this_tryout`, `can_manage_this_org_team`), en-têtes de sécurité complets et verrouillage de compte après 5 échecs.
|
|
|
|
Ces bonnes bases sont **annulées par une poignée de défauts de configuration et de déploiement**, pas par la logique métier. Les trois plus graves sont indépendants du code applicatif :
|
|
|
|
1. **Des secrets de production réels sont publiés dans le dépôt** (`app/.env.exemple`) : token du bot Discord, URL PostgreSQL Render complète avec mot de passe, `SECRET_KEY`. Ils sont dans l'historique git depuis le commit `2d3721b`.
|
|
2. **Un déploiement neuf s'auto-amorce avec des comptes `admin` / `password`** — le seed de démo se déclenche automatiquement en production quand la table `users` est vide.
|
|
3. **Le CORS par défaut accepte toutes les origines avec `supports_credentials=True`** dès que `CORS_ALLOWED_ORIGINS` n'est pas défini — ce qui est le cas dans le `.env.exemple` fourni.
|
|
|
|
Le README affirme « Authorization Checks: **Proper ownership validation on all sensitive operations** ». C'est vrai pour les tryouts, matchs et équipes, mais **faux pour la gestion des utilisateurs** : les trois routes `create_user`, `edit_user` et `edit_profile` importent des schémas de validation Marshmallow… et ne les appellent jamais. Aucune politique de mot de passe ne s'applique sur ces chemins.
|
|
|
|
Côté outillage, la CI est en trompe-l'œil : le job « Security Scan » pointe vers un fichier qui n'existe pas à cet emplacement, le job « Tests » est un `echo` en `continue-on-error`, et `requirements.txt` est encodé en **UTF-16**, ce qui casse `pip-audit`. Enfin, `.gitignore` contient `*.html` : **tout nouveau template créé est silencieusement ignoré par git**.
|
|
|
|
## Répartition des constats
|
|
|
|
| Sévérité | Sécurité | Maintenabilité | Standards | Total |
|
|
|---|---|---|---|---|
|
|
| 🔴 Critique | 4 | — | — | **4** |
|
|
| 🟠 Élevé | 4 | 4 | 1 | **9** |
|
|
| 🟡 Moyen | 7 | 7 | 6 | **20** |
|
|
| 🔵 Faible | 4 | 4 | 3 | **11** |
|
|
| **Total** | **19** | **15** | **10** | **44** |
|
|
|
|
## Les 5 actions à mener en premier
|
|
|
|
| # | Action | Détail |
|
|
|---|---|---|
|
|
| 1 | **Révoquer les 3 secrets exposés** puis purger l'historique git | [SEC-01](01-securite.md#sec-01--) |
|
|
| 2 | Désactiver le seed automatique en production | [SEC-02](01-securite.md#sec-02--) |
|
|
| 3 | Refuser le démarrage si `CORS_ALLOWED_ORIGINS` est vide | [SEC-03](01-securite.md#sec-03--) |
|
|
| 4 | Supprimer le `print()` du token Discord | [SEC-04](01-securite.md#sec-04--) |
|
|
| 5 | Brancher les schémas Marshmallow sur les routes utilisateurs | [SEC-06](01-securite.md#sec-06--) |
|
|
|
|
## Ce qui est déjà bien fait
|
|
|
|
Pour éviter de le casser lors des corrections :
|
|
|
|
- **Aucune injection SQL** — l'ORM est utilisé sans exception ; le seul `text()` est un `SELECT 1` de healthcheck.
|
|
- **Aucun `|safe`, aucun `{% autoescape false %}`** dans les templates ; les `innerHTML` de `main.js` ne manipulent que des chaînes littérales.
|
|
- **Autorisation par dispatch polymorphe** (`Admin`/`Manager`/`Coach`/`Player`/`Scout`) plutôt que par comparaison de chaînes de rôles — approche propre et testable.
|
|
- **Protection contre la fixation de session** au login (`session.clear()` avec préservation du token CSRF, ligne `auth.py:133-138`).
|
|
- **Validation anti-open-redirect** sur le paramètre `next` (`auth.py:23-36`).
|
|
- **En-têtes de sécurité complets** et HSTS conditionné à HTTPS réel.
|
|
- **Filtre de redaction des secrets dans les logs** (`logging_config.py:18-50`).
|
|
- **Découpage des modèles** en fichiers par entité, avec ordre d'import documenté par couches.
|