feat(v0.12.6): consolidated audit — 2 CRITICAL, 10 HIGH findings

Deep audit with 6 parallel analysis passes reveals additional findings:

CRITICAL:
- CRIT-001: IDOR on chat rooms — any user can read private conversations
- CRIT-002: play_count/like_count publicly exposed (violates VEZA ethics)

NEW HIGH:
- HIGH-004/005: Race conditions on promo codes and exclusive licenses
- HIGH-006: Rate limiter bypass via X-Forwarded-For (no TrustedProxies)
- HIGH-007: GDPR hard delete incomplete (Redis, ES, audit_logs)
- HIGH-008: RTMP callback auth fallback to stream_key as secret
- HIGH-009: Co-listening host hijack by non-host participants
- HIGH-010: Moderator can issue strikes without conflict-of-interest check

Total: 2 CRITICAL, 10 HIGH, 12 MEDIUM, 6 LOW, 5 INFO
Estimated remediation: ~39h30

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
senke 2026-03-11 15:44:51 +01:00
parent 1a38fdbcca
commit 41ae04679f
4 changed files with 345 additions and 33 deletions

View file

@ -73,7 +73,7 @@
| V4.1.1 | Trusted enforcement point | ✅ PASS | Middleware chain côté serveur uniquement | — |
| V4.1.2 | Access control on every request | ✅ PASS | AuthMiddleware + RBAC sur toutes les routes protégées | — |
| V4.1.3 | Principle of least privilege | ✅ PASS | Rôles granulaires (user, creator, premium, admin, moderator) | — |
| V4.2.1 | IDOR protection | ✅ PASS | RequireOwnershipOrAdmin sur tracks, playlists, products, messages | — |
| V4.2.1 | IDOR protection | ❌ FAIL | Chat rooms (GetRoom, GetRoomHistory) sans vérification membership | CRIT-001 |
| V4.2.2 | Prevent privilege escalation | ✅ PASS | Rôle vient du JWT → vérifié contre DB, pas modifiable par le client | — |
| V4.3.1 | Admin function protection | ✅ PASS | RequireAdmin middleware sur toutes les routes /admin/ | — |
| V4.3.2 | Directory listing disabled | ✅ PASS | API REST pure, pas de file serving sauf signedURL S3 | — |
@ -214,17 +214,18 @@
| Résultat | Nombre | % |
|----------|--------|---|
| ✅ PASS | 73 | 82% |
| ❌ FAIL | 2 | 2% |
| ✅ PASS | 72 | 80% |
| ❌ FAIL | 3 | 3% |
| ⚠️ PARTIEL | 13 | 15% |
| 🔍 NON VÉRIFIABLE | 3 | 3% |
| N/A | 1 | 1% |
**FAIL obligatoires** :
- V4.2.1 : IDOR protection → **CRIT-001** (chat rooms sans membership check)
- V2.6.1 : Recovery codes crypto/rand → **MEDIUM-001** (à corriger)
- V11.1.7 : Concurrency controls → **HIGH-001** (à corriger)
**Conclusion** : ASVS Level 2 est atteint à condition de corriger les 2 FAILs identifiés.
**Conclusion** : ASVS Level 2 NON atteint — 3 FAILs à corriger, dont 1 CRITIQUE (IDOR).
---

View file

@ -17,19 +17,19 @@
Le codebase VEZA est **globalement bien sécurisé** avec une architecture de sécurité mature (middleware chain, RBAC, token versioning, CSRF, rate limiting multi-couche). Les vulnérabilités critiques identifiées dans l'audit précédent (mars 2026) ont été corrigées (VEZA-SEC-001 JWT secret par défaut, VEZA-SEC-002 issuer/audience mismatch).
### Recommandation : **GO CONDITIONNEL**
### Recommandation : **NO-GO — Corriger les CRITIQUES et HAUTS avant v1.0.0**
L'audit identifie **0 finding CRITIQUE**, **3 findings HAUTS**, **8 findings MOYENS**, **6 findings BAS**, et **5 findings INFO**. Les findings HAUTS doivent être corrigés avant le GO v1.0.0.
L'audit approfondi (6 passes parallèles sur l'intégralité du codebase) identifie **2 findings CRITIQUES**, **10 findings HAUTS**, **12 findings MOYENS**, **6 findings BAS**, et **5 findings INFO**. Les findings CRITIQUES et HAUTS doivent être corrigés impérativement.
### Résumé des findings
| Sévérité | Nombre | Détail |
|----------|--------|--------|
| CRITIQUE | 0 | — |
| HAUTE | 3 | Race condition marketplace, production HS256, user repository context bypass |
| MOYENNE | 8 | Recovery codes math/rand, metrics IP spoofing, ClamAV latest, pagination sans limit max, WebSocket rate limit gaps, CORS Swagger CSP, CI actions non-pinnées, RabbitMQ mgmt exposé |
| BASSE | 6 | Password policy mismatch FE/BE, Hyperswitch version, dotenv crate, Elasticsearch sans auth, context.Background dans workers, exported debug endpoints |
| INFO | 5 | Bonnes pratiques confirmées, recommandations d'amélioration |
| CRITIQUE | 2 | IDOR chat rooms (lecture conversation privée sans membership), play_count/like_count exposés publiquement (violation éthique) |
| HAUTE | 10 | Race conditions marketplace (downloads, promo codes, licence exclusive), production HS256, IP spoofing rate limiter (pas de TrustedProxies), RGPD hard delete incomplet, RTMP callback auth faible, co-écoute host hijack, modérateur self-strike, free trial illimité |
| MOYENNE | 12 | Recovery codes math/rand, metrics IP spoofing, ClamAV latest, pagination sans limit max, WebSocket pas de re-auth, CSP Swagger, CI actions non-pinnées, RabbitMQ mgmt, password reset no rate limit, access token in response body, email dans logs, analytics sans k-anonymité |
| BASSE | 6 | Password policy mismatch FE/BE, Hyperswitch version, dotenv crate, Elasticsearch sans auth, context.Background dans workers, Redis sans auth dev |
| INFO | 5 | Bonnes pratiques confirmées (20+ contrôles), recommandations d'amélioration |
---
@ -37,6 +37,88 @@ L'audit identifie **0 finding CRITIQUE**, **3 findings HAUTS**, **8 findings MOY
---
### [CRIT-001] : IDOR — Lecture de conversations privées sans vérification de membership
**Sévérité** : CRITIQUE
**CVSS v3.1** : 9.1 (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)
**CWE** : CWE-639 — Authorization Bypass Through User-Controlled Key
**OWASP** : A01:2021 — Broken Access Control
**OWASP API** : API1:2023 — Broken Object Level Authorization
**Fichier(s)** : `veza-backend-api/internal/handlers/room_handler.go:134-158` (GetRoom), `:255-314` (GetRoomHistory)
**Composant** : Chat — Conversations
**Description**
Les endpoints `GET /api/v1/conversations/:id` et `GET /api/v1/conversations/:id/history` ne vérifient **pas** que l'utilisateur authentifié est membre de la room avant de retourner les données. N'importe quel utilisateur authentifié peut lire l'intégralité des messages de n'importe quelle conversation en connaissant son UUID.
**Preuve de concept**
```bash
# L'utilisateur B (non membre) accède à la conversation privée de l'utilisateur A
curl -H "Cookie: access_token=<jwt_user_B>" \
https://veza.fr/api/v1/conversations/<room_uuid_private>/history
# Retourne TOUS les messages de la conversation privée
```
**Impact**
- Confidentialité : Lecture de messages privés, données financières échangées, informations personnelles
- Compliance : Violation RGPD (accès non autorisé aux données personnelles)
- Business : Perte de confiance des utilisateurs, risque juridique
**Remédiation**
```go
func (h *RoomHandler) GetRoom(c *gin.Context) {
roomID, _ := uuid.Parse(c.Param("id"))
userID := c.MustGet("user_id").(uuid.UUID)
// SECURITY: Verify membership before returning room data
isMember, err := h.roomService.IsRoomMember(c.Request.Context(), roomID, userID)
if err != nil || !isMember {
RespondWithAppError(c, apperrors.NewNotFoundError("Conversation"))
return
}
// ... rest of handler
}
```
**Priorité de correction** : Immédiate (bloquant v1.0.0)
---
### [CRIT-002] : Métriques de popularité (play_count, like_count) exposées publiquement dans l'API
**Sévérité** : CRITIQUE (éthique VEZA)
**CVSS v3.1** : 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)
**CWE** : CWE-497 — Exposure of Sensitive System Information
**OWASP** : A01:2021 — Broken Access Control
**Fichier(s)** : `veza-backend-api/internal/models/track.go:39-40`
**Composant** : API — Tracks
**Description**
Le modèle `Track` expose `play_count` et `like_count` dans les réponses JSON via les tags `json:"play_count"` et `json:"like_count"`. Ces données sont retournées dans toutes les réponses de l'API publique (liste de tracks, détail de track, recherche).
Cela viole la **règle immuable #4 du CLAUDE.md** : *"JAMAIS de métriques de popularité publiques — Les likes et play counts sont PRIVÉS (visibles uniquement par le créateur dans ses analytics)"*.
**Impact**
- Violation du principe éthique fondamental de VEZA (pas de dark patterns FOMO)
- Crée un biais de popularité dans la découverte musicale
- Discrimine les artistes émergents
**Remédiation**
```go
// Créer un type de réponse publique sans les métriques
type TrackPublicResponse struct {
ID uuid.UUID `json:"id"`
Title string `json:"title"`
ArtistID uuid.UUID `json:"artist_id"`
// PAS de PlayCount ni LikeCount
}
// N'exposer play_count et like_count que dans /api/v1/me/tracks/:id/analytics (creator only)
```
**Priorité de correction** : Immédiate (bloquant v1.0.0)
---
### [HIGH-001] : Race condition TOCTOU sur le compteur de téléchargements marketplace
**Sévérité** : HAUTE
@ -152,6 +234,146 @@ Propager le context dans toute la chaîne d'appel (handler → service → repos
---
### [HIGH-004] : Race condition sur les codes promo (TOCTOU)
**Sévérité** : HAUTE
**CVSS v3.1** : 7.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)
**CWE** : CWE-362 — Concurrent Execution Using Shared Resource
**Fichier(s)** : `veza-backend-api/internal/core/marketplace/service.go:463-464,753-776`
**Composant** : Marketplace — Promo Codes
**Description**
La validation du code promo (`validatePromoCodeTx`) vérifie `UsedCount >= MaxUses` puis incrémente `used_count` séparément. Sans `SELECT FOR UPDATE`, deux requêtes concurrentes peuvent utiliser le même code promo au-delà de sa limite.
**Remédiation**
Ajouter `clause.Locking{Strength: "UPDATE"}` dans la requête de validation du code promo.
**Priorité de correction** : Sprint suivant
---
### [HIGH-005] : Pas de protection race condition sur licences exclusives
**Sévérité** : HAUTE
**CVSS v3.1** : 7.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)
**CWE** : CWE-362 — Concurrent Execution Using Shared Resource
**Fichier(s)** : `veza-backend-api/internal/core/marketplace/service.go:393-532`
**Composant** : Marketplace — Licenses
**Description**
Aucune vérification atomique n'empêche deux acheteurs d'acheter simultanément une licence exclusive du même produit. Deux transactions peuvent créer deux licences exclusives pour le même track.
**Remédiation**
Ajouter un check `SELECT FOR UPDATE` dans la transaction de création de licence :
```go
var existing License
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("product_id = ? AND type = 'exclusive' AND revoked_at IS NULL", prod.ID).
First(&existing).Error; err == nil {
return ErrExclusiveLicenseAlreadySold
}
```
**Priorité de correction** : Sprint suivant
---
### [HIGH-006] : Rate limiter bypass — TrustedProxies non configuré
**Sévérité** : HAUTE
**CVSS v3.1** : 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
**CWE** : CWE-346 — Origin Validation Error
**Fichier(s)** : `veza-backend-api/internal/middleware/rate_limiter.go:131,265,353,429`
**Composant** : Infrastructure — Rate Limiting
**Description**
Gin's `c.ClientIP()` fait confiance à `X-Forwarded-For` automatiquement. Sans appel explicite à `engine.SetTrustedProxies()`, n'importe quel client peut forger son IP et bypasser tous les rate limiters (DDoS, brute force, upload).
**Remédiation**
```go
engine.SetTrustedProxies([]string{"127.0.0.1", "10.0.0.0/8"}) // Adapter au proxy réel
```
**Priorité de correction** : Immédiate
---
### [HIGH-007] : RGPD hard delete incomplet (Redis, Elasticsearch, audit logs)
**Sévérité** : HAUTE
**CVSS v3.1** : 6.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
**CWE** : CWE-212 — Improper Cross-boundary Removal of Sensitive Data
**Fichier(s)** : `veza-backend-api/internal/workers/hard_delete_worker.go:101-121`
**Composant** : RGPD — Account Deletion
**Description**
Le hard delete worker anonymise uniquement `users` et `user_profiles` en DB. Les données PII restent dans : Redis (sessions/cache), Elasticsearch (index de recherche), audit_logs (user_id, IP), RabbitMQ (messages en queue).
**Remédiation**
Étendre le hard delete worker pour nettoyer : Redis keys, ES index, audit_logs anonymisation, RabbitMQ purge.
**Priorité de correction** : Sprint suivant
---
### [HIGH-008] : RTMP callback auth — fallback à query param "name" (stream key)
**Sévérité** : HAUTE
**CVSS v3.1** : 7.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)
**CWE** : CWE-287 — Improper Authentication
**Fichier(s)** : `veza-backend-api/internal/handlers/live_stream_callback.go:25-36`
**Composant** : Livestream — RTMP Callbacks
**Description**
La fonction `validateCallbackSecret()` fallback à `c.Query("name")` quand le header `X-RTMP-Callback-Secret` est absent. Le paramètre "name" contient en réalité le stream_key de l'utilisateur, pas le secret. Cela permet à quiconque connaissant un stream_key de déclencher des callbacks publish/publish_done.
De plus, si `RTMP_CALLBACK_SECRET` n'est pas configuré, la validation retourne `true` (fail-open).
**Remédiation**
- Supprimer le fallback à `c.Query("name")`
- Retourner `false` si le secret n'est pas configuré (fail-closed)
- Utiliser `hmac.Equal()` pour la comparaison
**Priorité de correction** : Sprint suivant
---
### [HIGH-009] : Co-écoute — participant non-host peut prendre le contrôle
**Sévérité** : HAUTE
**CVSS v3.1** : 6.5 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)
**CWE** : CWE-863 — Incorrect Authorization
**Fichier(s)** : `veza-backend-api/internal/websocket/colistening/hub.go:102-139`, `handlers/co_listening_websocket_handler.go:50-126`
**Composant** : Co-écoute WebSocket
**Description**
Le flag `IsHost` est défini à la connexion mais n'est pas re-vérifié lors du traitement des messages. Un listener peut envoyer un message `UpdateHostState` et son état de lecture sera broadcast comme autoritaire à tous les participants.
**Remédiation**
Vérifier `conn.IsHost` avant de traiter les messages de type `UpdateHostState`/`SyncClientStateMsg`.
**Priorité de correction** : Sprint suivant
---
### [HIGH-010] : Modérateur peut émettre des strikes sans contrôle de conflit d'intérêts
**Sévérité** : HAUTE
**CVSS v3.1** : 6.5 (AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:H/A:N)
**CWE** : CWE-863 — Incorrect Authorization
**Fichier(s)** : `veza-backend-api/internal/services/moderation_service.go:725-737`
**Composant** : Modération — Strikes
**Description**
Aucune vérification n'empêche un modérateur d'émettre un strike à lui-même ou de résoudre son propre appel. Un modérateur malveillant pourrait abuser du système pour cibler des utilisateurs spécifiques.
**Remédiation**
Ajouter la vérification : `if reportedUserID == moderatorID { return error }`.
**Priorité de correction** : Sprint suivant
---
### [MEDIUM-001] : Codes de récupération 2FA générés avec math/rand
**Sévérité** : MOYENNE
@ -398,6 +620,78 @@ En dev : accès au dashboard RabbitMQ exposant la topologie des queues et les me
---
### [MEDIUM-009] : Free trial réutilisable indéfiniment
**Sévérité** : MOYENNE
**CVSS v3.1** : 5.3 (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N)
**CWE** : CWE-863 — Incorrect Authorization
**Fichier(s)** : `veza-backend-api/internal/core/subscription/service.go:236-240`
**Composant** : Subscriptions
**Description**
Le code accorde un essai gratuit si `plan.TrialDays > 0` sans vérifier si l'utilisateur a déjà bénéficié d'un essai. Un utilisateur peut cycler entre free et premium pour obtenir des essais illimités.
**Remédiation**
Vérifier en DB si l'utilisateur a déjà eu un trial pour ce plan avant d'en accorder un nouveau.
**Priorité de correction** : Sprint suivant
---
### [MEDIUM-010] : WebSocket — pas de re-validation token après connexion
**Sévérité** : MOYENNE
**CVSS v3.1** : 5.4 (AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N)
**CWE** : CWE-613 — Insufficient Session Expiration
**Fichier(s)** : `veza-backend-api/internal/handlers/chat_websocket_handler.go:32-66`
**Composant** : Chat — WebSocket
**Description**
Le token JWT est validé uniquement à l'upgrade WebSocket. Si le token est révoqué (logout, ban), l'utilisateur reste connecté et peut continuer à envoyer/recevoir des messages.
**Remédiation**
Implémenter une re-validation périodique du token (toutes les 30 secondes) ou écouter les événements de révocation via Redis PubSub.
**Priorité de correction** : Sprint suivant
---
### [MEDIUM-011] : Email logué en clair dans les tentatives de login
**Sévérité** : MOYENNE
**CVSS v3.1** : 4.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)
**CWE** : CWE-532 — Insertion of Sensitive Information into Log File
**Fichier(s)** : `veza-backend-api/internal/handlers/auth.go:52-56`
**Composant** : Logging — Auth
**Description**
L'email de l'utilisateur est logué en clair lors des tentatives de login. Le `SecretFilterCore` filtre les champs `password`, `token`, `secret` mais pas `email`.
**Remédiation**
Ajouter `email` à la liste de redaction dans `secret_filter.go`, ou logger un hash de l'email.
**Priorité de correction** : Backlog
---
### [MEDIUM-012] : Analytics créateur sans k-anonymité (minimum 10 utilisateurs)
**Sévérité** : MOYENNE
**CVSS v3.1** : 4.3 (AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N)
**CWE** : CWE-359 — Exposure of Private Personal Information to an Unauthorized Actor
**Fichier(s)** : `veza-backend-api/internal/handlers/playback_analytics_handler.go`
**Composant** : Analytics — Privacy
**Description**
Les analytics créateur affichent des statistiques détaillées (heatmap, écoutes, géographie) même si un track n'a que 1-2 écoutes. Cela pourrait permettre de ré-identifier des auditeurs par recoupement.
**Remédiation**
Supprimer les analytics détaillées si `unique_listeners < 10`. Afficher uniquement des catégories agrégées.
**Priorité de correction** : Backlog
---
### [LOW-001] : Incohérence politique mot de passe frontend/backend
**Sévérité** : BASSE
@ -601,7 +895,7 @@ Aucun bloc `unsafe` trouvé dans le code applicatif du stream server Rust. Le co
| Catégorie | Verdict | Findings |
|-----------|---------|----------|
| A01 — Broken Access Control | ✅ PASS | RBAC, ownership checks, CORS strict |
| A01 — Broken Access Control | ❌ FAIL | CRIT-001 (IDOR rooms), CRIT-002 (play_count public), HIGH-009 (co-listen), HIGH-010 (modération) |
| A02 — Cryptographic Failures | ⚠️ PARTIEL | HIGH-002 (HS256 prod), MEDIUM-001 (math/rand 2FA) |
| A03 — Injection | ✅ PASS | GORM, ValidateExecPath, DOMPurify |
| A04 — Insecure Design | ⚠️ PARTIEL | HIGH-001 (race condition), HIGH-003 (context bypass) |
@ -616,7 +910,7 @@ Aucun bloc `unsafe` trouvé dans le code applicatif du stream server Rust. Le co
| Catégorie | Verdict | Findings |
|-----------|---------|----------|
| API1 — Broken Object Level Auth | ✅ PASS | RequireOwnershipOrAdmin sur toutes les ressources |
| API1 — Broken Object Level Auth | ❌ FAIL | CRIT-001 (IDOR rooms — pas de membership check) |
| API2 — Broken Authentication | ⚠️ PARTIEL | HIGH-002 (HS256), MEDIUM-001 (math/rand) |
| API3 — Broken Object Property Auth | ✅ PASS | Validation struct tags, pas de mass assignment |
| API4 — Unrestricted Resource Consumption | ⚠️ PARTIEL | MEDIUM-004 (pagination limit) |
@ -633,12 +927,12 @@ Aucun bloc `unsafe` trouvé dans le code applicatif du stream server Rust. Le co
| Critère | Statut | Détail |
|---------|--------|--------|
| Aucun finding CRITIQUE non résolu | ✅ GO | 0 finding critique |
| Aucun finding HAUT non résolu | ❌ NO-GO | 3 findings HAUTS à corriger |
| Tous les MOYENS ont un plan de remédiation | ✅ GO | 8 findings avec remédiation documentée |
| ASVS Level 2 sans FAIL obligatoire | ⚠️ CONDITIONNEL | Voir checklist ASVS séparée |
| Aucun finding CRITIQUE non résolu | ❌ NO-GO | 2 findings CRITIQUES à corriger (IDOR rooms, play_count public) |
| Aucun finding HAUT non résolu | ❌ NO-GO | 10 findings HAUTS à corriger |
| Tous les MOYENS ont un plan de remédiation | ✅ GO | 12 findings avec remédiation documentée |
| ASVS Level 2 sans FAIL obligatoire | ❌ NO-GO | 3 FAIL (IDOR, race condition, crypto/rand) |
**Décision : NO-GO — Corriger les 3 findings HAUTS avant v1.0.0**
**Décision : NO-GO — Corriger les 2 CRITIQUES + 10 HAUTS avant v1.0.0 (~30h d'effort total)**
---

View file

@ -9,9 +9,18 @@
| # | Finding | Sévérité | CVSS | Fichier(s) | Effort estimé | Priorité | Assignation suggérée | Statut |
|---|---------|----------|------|------------|---------------|----------|---------------------|--------|
| **CRIT-001** | **IDOR rooms — lecture conversations privées** | **CRITIQUE** | **9.1** | `room_handler.go:134-314` | 3h | **Immédiate** | Backend dev | ⏳ À FAIRE |
| **CRIT-002** | **play_count/like_count publics (violation éthique)** | **CRITIQUE** | **5.3** | `models/track.go:39-40` | 4h | **Immédiate** | Backend dev | ⏳ À FAIRE |
| HIGH-001 | Race condition TOCTOU downloads marketplace | HAUTE | 7.5 | `marketplace/service.go:794-817` | 2h | Immédiate | Backend dev | ⏳ À FAIRE |
| HIGH-002 | Production HS256 au lieu de RS256 | HAUTE | 7.4 | `docker-compose.prod.yml:158`, `jwt_service.go` | 4h | Immédiate | DevOps + Backend | ⏳ À FAIRE |
| HIGH-003 | User repository context.Background() bypass | HAUTE | 5.3 | `user_repository.go:125-150` | 4h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| HIGH-004 | Race condition codes promo | HAUTE | 7.5 | `marketplace/service.go:463,753` | 2h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| HIGH-005 | Race condition licence exclusive | HAUTE | 7.5 | `marketplace/service.go:393-532` | 2h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| HIGH-006 | Rate limiter bypass (TrustedProxies) | HAUTE | 7.5 | `rate_limiter.go:131` | 30min | Immédiate | Backend dev | ⏳ À FAIRE |
| HIGH-007 | RGPD hard delete incomplet | HAUTE | 6.5 | `hard_delete_worker.go:101` | 4h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| HIGH-008 | RTMP callback auth faible | HAUTE | 7.3 | `live_stream_callback.go:25-36` | 1h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| HIGH-009 | Co-écoute host hijack | HAUTE | 6.5 | `colistening/hub.go:102` | 1h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| HIGH-010 | Modérateur self-strike | HAUTE | 6.5 | `moderation_service.go:725` | 1h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| MEDIUM-001 | Recovery codes 2FA avec math/rand | MOYENNE | 5.9 | `two_factor_service.go:200` | 30min | Sprint suivant | Backend dev | ⏳ À FAIRE |
| MEDIUM-002 | Metrics IP spoofing via X-Forwarded-For | MOYENNE | 5.3 | `metrics_protection.go:52-54` | 15min | Sprint suivant | Backend dev | ⏳ À FAIRE |
| MEDIUM-004 | Pagination sans limite maximale | MOYENNE | 5.3 | Pagination middleware + handlers | 2h | Sprint suivant | Backend dev | ⏳ À FAIRE |
@ -20,6 +29,10 @@
| MEDIUM-007 | CI actions non pinnées par SHA | MOYENNE | 4.8 | `.github/workflows/*.yml` | 1h | Sprint suivant | DevOps | ⏳ À FAIRE |
| MEDIUM-006 | CSP unsafe-inline Swagger routes | MOYENNE | 4.7 | `security_headers.go:78` | 30min | Backlog | Backend dev | ⏳ À FAIRE |
| MEDIUM-008 | RabbitMQ Management UI en prod | MOYENNE | 5.3 | `docker-compose.prod.yml` | 15min | Backlog | DevOps | ⏳ À FAIRE |
| MEDIUM-009 | Free trial réutilisable indéfiniment | MOYENNE | 5.3 | `subscription/service.go:236` | 1h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| MEDIUM-010 | WebSocket pas de re-validation token | MOYENNE | 5.4 | `chat_websocket_handler.go:32` | 2h | Sprint suivant | Backend dev | ⏳ À FAIRE |
| MEDIUM-011 | Email logué en clair | MOYENNE | 4.3 | `handlers/auth.go:52` | 30min | Backlog | Backend dev | ⏳ À FAIRE |
| MEDIUM-012 | Analytics sans k-anonymité | MOYENNE | 4.3 | `playback_analytics_handler.go` | 1h30 | Backlog | Backend dev | ⏳ À FAIRE |
| LOW-001 | Password policy mismatch FE/BE | BASSE | 3.7 | `passwordValidator.ts` | 30min | Backlog | Frontend dev | ⏳ À FAIRE |
| LOW-002 | Hyperswitch version datée | BASSE | 3.1 | `docker-compose*.yml` | 2h (+ tests) | Backlog | DevOps | ⏳ À FAIRE |
| LOW-003 | dotenv 0.15 obsolète (Rust) | BASSE | 2.0 | `Cargo.toml` | 30min | Backlog | Backend dev | ⏳ À FAIRE |
@ -33,29 +46,33 @@
| Priorité | Nombre de findings | Effort total estimé |
|----------|--------------------|---------------------|
| Immédiate (avant v1.0.0) | 2 | ~6h |
| Sprint suivant | 6 | ~4h30 |
| Backlog | 8 | ~7h |
| **Total** | **16** | **~17h30** |
| Immédiate (bloquant v1.0.0) | 5 | ~13h30 |
| Sprint suivant | 15 | ~18h |
| Backlog | 10 | ~8h |
| **Total** | **30** | **~39h30** |
---
## Plan de remédiation recommandé
### Phase 1 — Immédiate (avant merge en main)
1. **HIGH-002** : Générer paire RSA 2048-bit, configurer `docker-compose.prod.yml` avec `JWT_PRIVATE_KEY_PATH` / `JWT_PUBLIC_KEY_PATH`, supprimer `JWT_SECRET` en prod
2. **HIGH-001** : Wrapper `GetDownloadURL` dans une transaction avec `SELECT FOR UPDATE`
### Phase 1 — Immédiate (bloquant — avant merge en main)
1. **CRIT-001** : Ajouter vérification membership sur `GetRoom` et `GetRoomHistory`
2. **CRIT-002** : Supprimer `play_count`/`like_count` des réponses API publiques
3. **HIGH-002** : Migrer production vers RS256 (paire RSA 2048-bit)
4. **HIGH-006** : Configurer `engine.SetTrustedProxies()` pour le rate limiter
5. **HIGH-001** : Transaction + `SELECT FOR UPDATE` sur `GetDownloadURL`
### Phase 2 — Sprint suivant
3. **HIGH-003** : Refactorer `UserRepository` pour accepter `context.Context` sur toutes les méthodes
4. **MEDIUM-001** : Remplacer `mathrand.Intn` par `crypto/rand.Int` dans `two_factor_service.go`
5. **MEDIUM-002** : Supprimer la lecture de `X-Forwarded-For` dans `metrics_protection.go`
6. **MEDIUM-003** : Pinner ClamAV à une version spécifique
7. **MEDIUM-004** : Ajouter `MaxPageSize = 100` dans le middleware de pagination
8. **MEDIUM-007** : Pinner les actions CI par SHA commit
6. **HIGH-004/005** : Protéger codes promo et licences exclusives contre race conditions
7. **HIGH-007** : Compléter le hard delete RGPD (Redis, ES, audit_logs)
8. **HIGH-008** : Corriger RTMP callback auth (supprimer fallback query param)
9. **HIGH-009/010** : Corriger co-écoute et modération
10. **MEDIUM-001** : crypto/rand pour recovery codes 2FA
11. **MEDIUM-009/010** : Free trial + WebSocket re-auth
12-15. Autres MEDIUM
### Phase 3 — Backlog
9-16. Corrections restantes (LOW + MEDIUM backlog)
16-30. Corrections LOW + MEDIUM backlog
---

View file

@ -1127,7 +1127,7 @@ Référence : ORIGIN_REVISION_SUMMARY.md §6 Incohérence #2
- [x] Rapport de remédiation documenté : `REMEDIATION_MATRIX_v0.12.6.md`
- [x] Checklist ASVS Level 2 : `ASVS_CHECKLIST_v0.12.6.md`
**Résultat de l'audit** : 0 CRITIQUE, 3 HAUTE, 8 MOYENNE, 6 BASSE, 5 INFO. GO CONDITIONNEL (corriger les 3 HAUTE avant v1.0.0)
**Résultat de l'audit** : 2 CRITIQUE, 10 HAUTE, 12 MOYENNE, 6 BASSE, 5 INFO. NO-GO (corriger les 2 CRITIQUES + 10 HAUTS avant v1.0.0 — ~30h)
---