758 lines
22 KiB
Markdown
758 lines
22 KiB
Markdown
|
|
# 🔍 AUDIT DE STABILITÉ — PROJET VEZA
|
||
|
|
|
||
|
|
**Date** : 2025-01-27
|
||
|
|
**Objectif** : Identifier toutes les faiblesses potentielles dans la robustesse, cohérence, performances et résilience du système
|
||
|
|
**Phase** : Zero-Bug / Launch-Ready
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📋 TABLE DES MATIÈRES
|
||
|
|
|
||
|
|
1. [Backend Go](#1-backend-go)
|
||
|
|
2. [Chat Server (Rust)](#2-chat-server-rust)
|
||
|
|
3. [Stream Server (Rust)](#3-stream-server-rust)
|
||
|
|
4. [Global Project](#4-global-project)
|
||
|
|
5. [Résumé des Risques](#5-résumé-des-risques)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. BACKEND GO
|
||
|
|
|
||
|
|
### 1.1 Handlers HTTP
|
||
|
|
|
||
|
|
#### ✅ **P0 - Erreurs JSON non traitées silencieusement** — **RÉSOLU**
|
||
|
|
|
||
|
|
**Localisation** : `internal/handlers/common.go:280-287`
|
||
|
|
|
||
|
|
**Status** : ✅ **RÉSOLU** — Phase 4 JSON Hardening complétée
|
||
|
|
|
||
|
|
**Solution implémentée** :
|
||
|
|
- Création de `BindAndValidateJSON` dans `CommonHandler` avec :
|
||
|
|
- Vérification de la taille du body (10MB max)
|
||
|
|
- Gestion robuste des erreurs JSON (syntaxe, type, body vide, etc.)
|
||
|
|
- Validation automatique avec le validator centralisé
|
||
|
|
- Retour d'`AppError` au lieu d'erreurs génériques
|
||
|
|
- Tous les handlers dans `internal/handlers/` refactorisés pour utiliser `BindAndValidateJSON` + `RespondWithAppError`
|
||
|
|
- Handlers critiques refactorisés : auth, social, marketplace, playlists, profile, comment, role, analytics, bitrate, settings, room, webhook, config_reload, password_reset
|
||
|
|
|
||
|
|
**Impact** : Plus aucune erreur JSON ne passe silencieusement. Toutes les erreurs de parsing/validation sont renvoyées avec un format unifié et des codes HTTP appropriés.
|
||
|
|
|
||
|
|
**Note** : Il reste ~26 occurrences dans `internal/api/` (handlers dans des packages différents utilisant des patterns différents). À refactoriser dans une phase ultérieure si nécessaire.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Erreurs silencieuses dans les handlers**
|
||
|
|
|
||
|
|
**Localisation** : `internal/handlers/auth.go`, `internal/handlers/social.go`
|
||
|
|
|
||
|
|
**Problème** : Certains handlers retournent des erreurs génériques sans contexte suffisant. Exemple :
|
||
|
|
|
||
|
|
```go
|
||
|
|
if err != nil {
|
||
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"})
|
||
|
|
return
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impact** : Difficile de diagnostiquer les problèmes en production.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser systématiquement `RespondWithAppError` avec contexte enrichi.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Validation d'input incomplète**
|
||
|
|
|
||
|
|
**Localisation** : Tous les handlers
|
||
|
|
|
||
|
|
**Problème** : Certains handlers n'utilisent pas `ValidateRequest` avant de traiter les données.
|
||
|
|
|
||
|
|
**Impact** : Risque d'injection SQL, XSS, ou corruption de données.
|
||
|
|
|
||
|
|
**Recommandation** : Middleware de validation automatique pour toutes les routes POST/PUT.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 1.2 Base de données
|
||
|
|
|
||
|
|
#### ❌ **P0 - Absence de transactions dans certaines opérations critiques**
|
||
|
|
|
||
|
|
**Localisation** : `internal/core/marketplace/service.go:134-136`
|
||
|
|
|
||
|
|
**Problème** : `CreateOrder` utilise une transaction, mais d'autres opérations multi-étapes non :
|
||
|
|
|
||
|
|
```go
|
||
|
|
// Exemple problématique (si non transactionnel)
|
||
|
|
func (s *Service) UpdateUserProfile(ctx context.Context, userID uuid.UUID, profile *Profile) error {
|
||
|
|
// Étape 1: Mise à jour user
|
||
|
|
s.db.Update(&user)
|
||
|
|
// Étape 2: Mise à jour profile
|
||
|
|
s.db.Update(&profile)
|
||
|
|
// Si étape 2 échoue, étape 1 reste appliquée → INCOHÉRENCE
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impact** : Incohérence DB en cas d'erreur partielle.
|
||
|
|
|
||
|
|
**Recommandation** : Audit complet des opérations multi-étapes, wrapper dans transactions.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Erreurs DB non wrap**
|
||
|
|
|
||
|
|
**Localisation** : Plusieurs services
|
||
|
|
|
||
|
|
**Problème** : Certaines erreurs DB sont retournées directement sans contexte :
|
||
|
|
|
||
|
|
```go
|
||
|
|
if err := s.db.First(&user, "id = ?", id).Error; err != nil {
|
||
|
|
return nil, err // Pas de contexte
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Impact** : Debugging difficile, pas de traçabilité.
|
||
|
|
|
||
|
|
**Recommandation** : Toujours wrapper avec `fmt.Errorf("failed to find user %s: %w", id, err)`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de retry automatique pour les erreurs transitoires**
|
||
|
|
|
||
|
|
**Localisation** : Tous les appels DB
|
||
|
|
|
||
|
|
**Problème** : Pas de retry automatique pour `database/sql` errors (timeouts, connection pool exhausted).
|
||
|
|
|
||
|
|
**Impact** : Échecs temporaires non récupérés automatiquement.
|
||
|
|
|
||
|
|
**Recommandation** : Wrapper DB avec retry logic (exponential backoff) pour erreurs transitoires.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 1.3 Workers
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Race condition potentielle lors des retries**
|
||
|
|
|
||
|
|
**Localisation** : `internal/workers/job_worker.go:127-135`
|
||
|
|
|
||
|
|
```go
|
||
|
|
if job.Retries < w.maxRetries {
|
||
|
|
job.Retries++
|
||
|
|
delay := time.Duration(job.Retries) * 5 * time.Second
|
||
|
|
time.Sleep(delay) // ⚠️ Bloque le worker
|
||
|
|
w.Enqueue(job) // ⚠️ Pas de lock sur job
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si plusieurs workers tentent de retry le même job simultanément, `Retries` peut être incrémenté plusieurs fois.
|
||
|
|
|
||
|
|
**Impact** : Jobs retry plus que `maxRetries`, ou jobs dupliqués dans la queue.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser un mutex ou atomic operations pour `job.Retries`, ou marquer le job comme "retrying" en DB avant ré-enqueue.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de timeout explicite pour les jobs**
|
||
|
|
|
||
|
|
**Localisation** : `internal/workers/job_worker.go:116`
|
||
|
|
|
||
|
|
```go
|
||
|
|
jobCtx, cancel := context.WithTimeout(ctx, 5*time.Minute)
|
||
|
|
defer cancel()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Timeout hardcodé, pas configurable. Si un job prend plus de 5 minutes, il est annulé brutalement.
|
||
|
|
|
||
|
|
**Impact** : Jobs longs (ex: transcodage) peuvent être interrompus.
|
||
|
|
|
||
|
|
**Recommandation** : Timeout configurable par type de job.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P2 - Queue in-memory sans persistance**
|
||
|
|
|
||
|
|
**Localisation** : `internal/workers/job_worker.go`
|
||
|
|
|
||
|
|
**Problème** : La queue est en mémoire (`chan Job`). Si le serveur crash, les jobs en attente sont perdus.
|
||
|
|
|
||
|
|
**Impact** : Perte de jobs non traités lors d'un crash.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser une queue persistante (Redis, RabbitMQ) pour les jobs critiques.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 1.4 Password Reset
|
||
|
|
|
||
|
|
#### ✅ **Bien protégé contre l'énumération**
|
||
|
|
|
||
|
|
**Localisation** : `internal/core/auth/service.go:372-379`
|
||
|
|
|
||
|
|
```go
|
||
|
|
if err == gorm.ErrRecordNotFound {
|
||
|
|
return nil // Toujours retourner succès
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status** : ✅ Implémentation correcte — toujours retourner succès même si email n'existe pas.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Timing attack potentiel**
|
||
|
|
|
||
|
|
**Localisation** : `internal/services/password_reset_service.go:70-125`
|
||
|
|
|
||
|
|
**Problème** : Le temps de traitement peut différer entre :
|
||
|
|
- Email existe → Génération token + Hash + DB write
|
||
|
|
- Email n'existe pas → Simple DB query
|
||
|
|
|
||
|
|
**Impact** : Attaquant peut détecter si un email existe via timing.
|
||
|
|
|
||
|
|
**Recommandation** : Ajouter un délai artificiel pour égaliser les temps de réponse.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 1.5 Health Check
|
||
|
|
|
||
|
|
#### ✅ **Robuste si DB en panne**
|
||
|
|
|
||
|
|
**Localisation** : `internal/handlers/health.go:70-77`, `internal/handlers/status_handler.go`
|
||
|
|
|
||
|
|
**Status** : ✅ `/health` est stateless (toujours OK). `/status` gère correctement les erreurs DB et retourne `degraded`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P2 - Pas de circuit breaker**
|
||
|
|
|
||
|
|
**Localisation** : Health checks
|
||
|
|
|
||
|
|
**Problème** : Si DB est down, chaque health check tente une connexion (timeout 5s). Pas de circuit breaker pour éviter de surcharger DB.
|
||
|
|
|
||
|
|
**Impact** : Si DB est down, health checks continuent à tenter des connexions.
|
||
|
|
|
||
|
|
**Recommandation** : Implémenter un circuit breaker pour les dépendances externes.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. CHAT SERVER (RUST)
|
||
|
|
|
||
|
|
### 2.1 Race Conditions
|
||
|
|
|
||
|
|
#### ❌ **P0 - Race condition dans TypingIndicatorManager**
|
||
|
|
|
||
|
|
**Localisation** : `src/typing_indicator.rs:34-48`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
pub async fn user_started_typing(&self, user_id: Uuid, conversation_id: Uuid) {
|
||
|
|
let mut typing = self.typing_users.write().await;
|
||
|
|
let conversation_typing = typing
|
||
|
|
.entry(conversation_id)
|
||
|
|
.or_insert_with(HashMap::new);
|
||
|
|
conversation_typing.insert(user_id, Utc::now());
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Le `RwLock` protège la HashMap, mais si deux utilisateurs tapent simultanément dans la même conversation, l'ordre d'insertion peut varier.
|
||
|
|
|
||
|
|
**Impact** : Timestamps peuvent être inversés, causant des broadcasts dans le mauvais ordre.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser un `Mutex` au lieu de `RwLock` pour garantir l'ordre, ou utiliser un canal sérialisé.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Race condition dans DeliveredStatusManager**
|
||
|
|
|
||
|
|
**Localisation** : `src/delivered_status.rs`
|
||
|
|
|
||
|
|
**Problème** : Si plusieurs messages sont marqués comme "delivered" simultanément, les updates DB peuvent se chevaucher.
|
||
|
|
|
||
|
|
**Impact** : Statuts de livraison incohérents.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser une queue sérialisée pour les updates de statut.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Race condition dans ReadReceiptManager**
|
||
|
|
|
||
|
|
**Localisation** : `src/read_receipts.rs`
|
||
|
|
|
||
|
|
**Problème** : Même problème que DeliveredStatusManager.
|
||
|
|
|
||
|
|
**Recommandation** : Queue sérialisée ou transaction DB.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.2 Panics Potentiels
|
||
|
|
|
||
|
|
#### ❌ **P0 - Panics dans WebSocket handler**
|
||
|
|
|
||
|
|
**Localisation** : `src/websocket/handler.rs:175-176`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
let incoming: IncomingMessage = serde_json::from_str(text)
|
||
|
|
.map_err(|e| ChatError::serialization_error("IncomingMessage", text, e))?;
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status** : ✅ Bien géré — erreur retournée, pas de panic.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - `.unwrap()` dans plusieurs fichiers**
|
||
|
|
|
||
|
|
**Localisation** : 31 fichiers identifiés avec `unwrap()` ou `expect()`
|
||
|
|
|
||
|
|
**Exemples** :
|
||
|
|
- `src/config.rs` : `unwrap()` sur variables d'environnement
|
||
|
|
- `src/database/pool.rs` : `unwrap()` sur connexions DB
|
||
|
|
- `src/jwt_manager.rs` : `expect()` sur parsing JWT
|
||
|
|
|
||
|
|
**Impact** : Panics possibles si données inattendues.
|
||
|
|
|
||
|
|
**Recommandation** : Remplacer tous les `unwrap()` par `?` ou gestion d'erreur explicite.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de panic boundary dans handle_socket**
|
||
|
|
|
||
|
|
**Localisation** : `src/websocket/handler.rs:77-163`
|
||
|
|
|
||
|
|
**Problème** : Si une panic survient dans `handle_incoming_message`, elle peut faire crasher toute la task Tokio.
|
||
|
|
|
||
|
|
**Impact** : Un client malveillant peut faire crasher le serveur.
|
||
|
|
|
||
|
|
**Recommandation** : Wrapper `handle_incoming_message` dans `std::panic::catch_unwind` ou utiliser `tokio::spawn` avec supervision.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.3 Gestion des Tasks
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Tasks orphelins possibles**
|
||
|
|
|
||
|
|
**Localisation** : `src/typing_indicator.rs` (task de monitoring)
|
||
|
|
|
||
|
|
**Problème** : La task de monitoring des timeouts est spawnée au démarrage mais n'a pas de mécanisme de shutdown propre.
|
||
|
|
|
||
|
|
**Impact** : Task continue à tourner même après arrêt du serveur.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser un `CancellationToken` pour arrêter proprement les tasks.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de timeout explicite pour les opérations DB**
|
||
|
|
|
||
|
|
**Localisation** : Tous les appels DB
|
||
|
|
|
||
|
|
**Problème** : Pas de timeout sur les queries SQLx. Si DB est lente, les requêtes peuvent bloquer indéfiniment.
|
||
|
|
|
||
|
|
**Impact** : Deadlock ou timeout très long.
|
||
|
|
|
||
|
|
**Recommandation** : Ajouter des timeouts sur tous les appels DB (via `sqlx::query().fetch_timeout()`).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.4 Robustesse WebSocket
|
||
|
|
|
||
|
|
#### ✅ **Bien géré — déconnexions propres**
|
||
|
|
|
||
|
|
**Localisation** : `src/websocket/handler.rs:134-137`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
Ok(Message::Close(_)) => {
|
||
|
|
info!("👋 Connexion WebSocket fermée par le client");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status** : ✅ Déconnexions gérées proprement.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de heartbeat timeout**
|
||
|
|
|
||
|
|
**Localisation** : `src/websocket/handler.rs`
|
||
|
|
|
||
|
|
**Problème** : Pas de mécanisme pour détecter les connexions "zombies" (client déconnecté mais serveur ne le sait pas).
|
||
|
|
|
||
|
|
**Impact** : Connexions mortes occupent des ressources.
|
||
|
|
|
||
|
|
**Recommandation** : Implémenter un heartbeat (ping/pong) avec timeout.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2.5 Permissions
|
||
|
|
|
||
|
|
#### ✅ **Bien implémenté — PermissionService**
|
||
|
|
|
||
|
|
**Localisation** : `src/security/permission.rs`
|
||
|
|
|
||
|
|
**Status** : ✅ Vérifications de permissions présentes avant chaque action.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Risque de bypass si PermissionService échoue**
|
||
|
|
|
||
|
|
**Localisation** : `src/websocket/handler.rs:194-200`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
state
|
||
|
|
.permission_service
|
||
|
|
.can_send_message(sender_uuid, conversation_id)
|
||
|
|
.await
|
||
|
|
.map_err(|e| {
|
||
|
|
warn!(...);
|
||
|
|
// ⚠️ Que se passe-t-il si l'erreur est ignorée ?
|
||
|
|
})?;
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si `can_send_message` retourne une erreur, elle est loggée mais le handler peut continuer selon l'implémentation.
|
||
|
|
|
||
|
|
**Impact** : Bypass de permissions si erreur DB.
|
||
|
|
|
||
|
|
**Recommandation** : Toujours refuser l'action si permission check échoue (fail-secure).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. STREAM SERVER (RUST)
|
||
|
|
|
||
|
|
### 3.1 StreamProcessor
|
||
|
|
|
||
|
|
#### ❌ **P0 - Tasks non cancellées proprement en cas d'erreur**
|
||
|
|
|
||
|
|
**Localisation** : `src/core/processing/processor.rs:168-169`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
monitor_handle.abort();
|
||
|
|
event_handle.abort();
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : `abort()` tue brutalement les tasks. Si elles étaient en train d'écrire en DB, la transaction peut rester ouverte.
|
||
|
|
|
||
|
|
**Impact** : Handles orphelins, transactions DB non commitées.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser `CancellationToken` pour arrêter proprement, attendre la fin des tasks avant `abort()`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Erreurs FFmpeg non propagées correctement**
|
||
|
|
|
||
|
|
**Localisation** : `src/core/processing/processor.rs:154-156`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
FFmpegEvent::Error(msg) => {
|
||
|
|
tracing::warn!("⚠️ Erreur FFmpeg détectée: {}", msg);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Les erreurs FFmpeg sont loggées mais ne causent pas l'arrêt du traitement. Le job continue même si FFmpeg a une erreur fatale.
|
||
|
|
|
||
|
|
**Impact** : Jobs peuvent se terminer en "succès" alors que FFmpeg a échoué.
|
||
|
|
|
||
|
|
**Recommandation** : Détecter les erreurs fatales FFmpeg et arrêter le traitement immédiatement.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - DB pas toujours sync en cas de crash**
|
||
|
|
|
||
|
|
**Localisation** : `src/core/processing/processor.rs:238-243`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
async fn finalize(&self, tracker: Arc<SegmentTracker>) -> Result<(), AppError> {
|
||
|
|
tracker.persist_all().await?;
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si le serveur crash avant `finalize()`, les segments détectés mais non persistés sont perdus.
|
||
|
|
|
||
|
|
**Impact** : Incohérence entre fichiers segments et DB.
|
||
|
|
|
||
|
|
**Recommandation** : Persister immédiatement chaque segment (déjà fait dans `SegmentTracker::register`), mais vérifier que c'est bien transactionnel.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3.2 SegmentTracker
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Corruption d'état concurrent possible**
|
||
|
|
|
||
|
|
**Localisation** : `src/core/processing/segment_tracker.rs:59-78`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
pub async fn register(&self, segment: SegmentInfo) -> Result<(), AppError> {
|
||
|
|
{
|
||
|
|
let mut segments = self.segments.write().await;
|
||
|
|
segments.push(segment.clone());
|
||
|
|
}
|
||
|
|
self.persist_segment(&segment).await?;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si deux segments sont enregistrés simultanément, l'ordre d'insertion dans le vecteur peut varier, mais la persistance DB se fait séquentiellement.
|
||
|
|
|
||
|
|
**Impact** : Segments peuvent être persistés dans le mauvais ordre.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser un canal sérialisé pour les registrations, ou un mutex global.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3.3 FFmpegMonitor
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Regex non robustes**
|
||
|
|
|
||
|
|
**Localisation** : `src/core/processing/ffmpeg_monitor.rs:22-24`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
static ref OPENING_SEGMENT_REGEX: Regex = Regex::new(
|
||
|
|
r"Opening '([^']+)' for writing"
|
||
|
|
).unwrap();
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si FFmpeg change son format de log, la regex ne matchera plus. Pas de fallback.
|
||
|
|
|
||
|
|
**Impact** : Segments non détectés, job échoue silencieusement.
|
||
|
|
|
||
|
|
**Recommandation** : Ajouter un fallback : détecter les segments depuis le répertoire de sortie si regex échoue.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Gestion des IO errors incomplète**
|
||
|
|
|
||
|
|
**Localisation** : `src/core/processing/ffmpeg_monitor.rs:90-94`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
while let Ok(Some(line)) = lines.next_line().await {
|
||
|
|
self.process_line(&line).await?;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si `next_line()` retourne une erreur (ex: stderr fermé), la boucle s'arrête silencieusement.
|
||
|
|
|
||
|
|
**Impact** : Monitoring s'arrête sans notification, job continue mais plus de tracking.
|
||
|
|
|
||
|
|
**Recommandation** : Logger l'erreur et propager pour arrêter le job.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3.4 API HLS
|
||
|
|
|
||
|
|
#### ✅ **Path traversal protégé**
|
||
|
|
|
||
|
|
**Localisation** : `src/routes/encoding.rs:128-133`, `internal/services/hls_service.go:137-151`
|
||
|
|
|
||
|
|
**Status** : ✅ Vérification du chemin absolu avec `HasPrefix` pour éviter path traversal.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Erreurs HTTP silencieuses**
|
||
|
|
|
||
|
|
**Localisation** : `src/routes/encoding.rs:144-148`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
if !segment_path.exists() {
|
||
|
|
return Err(AppError::NotFound { ... });
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si le fichier existe mais n'est pas lisible (permissions), l'erreur sera générique.
|
||
|
|
|
||
|
|
**Impact** : Debugging difficile.
|
||
|
|
|
||
|
|
**Recommandation** : Différencier "not found" vs "permission denied" vs "IO error".
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. GLOBAL PROJECT
|
||
|
|
|
||
|
|
### 4.1 Cohérence Inter-Services
|
||
|
|
|
||
|
|
#### ❌ **P0 - Pas de transaction distribuée**
|
||
|
|
|
||
|
|
**Localisation** : Tous les services
|
||
|
|
|
||
|
|
**Problème** : Si un message est créé dans le chat server mais que le backend Go échoue à créer une notification, les deux DB sont incohérentes.
|
||
|
|
|
||
|
|
**Impact** : Données incohérentes entre services.
|
||
|
|
|
||
|
|
**Recommandation** : Implémenter un pattern Saga ou Event Sourcing pour garantir la cohérence.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de validation croisée des IDs**
|
||
|
|
|
||
|
|
**Localisation** : Communication inter-services
|
||
|
|
|
||
|
|
**Problème** : Le chat server accepte des `conversation_id` sans vérifier qu'ils existent dans le backend Go.
|
||
|
|
|
||
|
|
**Impact** : Messages peuvent être créés pour des conversations inexistantes.
|
||
|
|
|
||
|
|
**Recommandation** : Validation croisée via API ou cache partagé.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4.2 Tests
|
||
|
|
|
||
|
|
#### ❌ **P0 - Manque de tests unitaires critiques**
|
||
|
|
|
||
|
|
**Localisation** : Tous les services
|
||
|
|
|
||
|
|
**Problème** : Beaucoup de tests sont `#[ignore]` car nécessitent une DB de test.
|
||
|
|
|
||
|
|
**Impact** : Pas de validation automatique des corrections.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser des mocks (ex: `sqlx::test`) ou des containers Docker pour les tests.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de tests de charge**
|
||
|
|
|
||
|
|
**Localisation** : Aucun
|
||
|
|
|
||
|
|
**Problème** : Pas de validation que le système supporte 100+ clients simultanés.
|
||
|
|
|
||
|
|
**Impact** : Problèmes de performance non détectés.
|
||
|
|
|
||
|
|
**Recommandation** : Tests de charge avec k6 ou locust.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4.3 Fuites Goroutine / Tokio Task
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Goroutines sans mécanisme de shutdown**
|
||
|
|
|
||
|
|
**Localisation** : `internal/jobs/cleanup_sessions.go:33-45`
|
||
|
|
|
||
|
|
```go
|
||
|
|
go func() {
|
||
|
|
for range ticker.C {
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Pas de moyen d'arrêter cette goroutine proprement.
|
||
|
|
|
||
|
|
**Impact** : Goroutine continue après arrêt du serveur.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser `context.Context` avec cancellation.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Tokio tasks spawnées sans supervision**
|
||
|
|
|
||
|
|
**Localisation** : `veza-chat-server/src/optimized_persistence.rs:264-285`
|
||
|
|
|
||
|
|
```rust
|
||
|
|
tokio::spawn(async move {
|
||
|
|
engine_clone.batch_processing_loop().await;
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problème** : Si la task panic, elle n'est pas relancée.
|
||
|
|
|
||
|
|
**Impact** : Service peut s'arrêter silencieusement.
|
||
|
|
|
||
|
|
**Recommandation** : Utiliser un supervisor task qui relance les tasks en cas de panic.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4.4 Logging Contextuel
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de correlation-id systématique**
|
||
|
|
|
||
|
|
**Localisation** : Tous les services
|
||
|
|
|
||
|
|
**Problème** : Pas de `correlation-id` ou `trace-id` pour suivre une requête à travers les services.
|
||
|
|
|
||
|
|
**Impact** : Debugging difficile en production.
|
||
|
|
|
||
|
|
**Recommandation** : Implémenter OpenTelemetry ou un système de tracing distribué.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P2 - Logs non structurés dans certains endroits**
|
||
|
|
|
||
|
|
**Localisation** : Quelques handlers
|
||
|
|
|
||
|
|
**Problème** : Certains logs utilisent `fmt.Printf` au lieu de `tracing` ou `zap`.
|
||
|
|
|
||
|
|
**Impact** : Logs non queryables.
|
||
|
|
|
||
|
|
**Recommandation** : Standardiser sur `tracing` (Rust) et `zap` (Go).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4.5 Risques d'Incohérence DB
|
||
|
|
|
||
|
|
#### ❌ **P0 - Jobs, messages, segments peuvent être incohérents**
|
||
|
|
|
||
|
|
**Localisation** : Tous les services
|
||
|
|
|
||
|
|
**Problème** : Si un job de transcodage échoue après avoir créé des segments en DB, les segments restent orphelins.
|
||
|
|
|
||
|
|
**Impact** : DB contient des données incohérentes.
|
||
|
|
|
||
|
|
**Recommandation** : Jobs de cleanup périodiques pour supprimer les données orphelines.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
#### ⚠️ **P1 - Pas de vérification d'intégrité**
|
||
|
|
|
||
|
|
**Localisation** : Aucun
|
||
|
|
|
||
|
|
**Problème** : Pas de job qui vérifie que les fichiers segments correspondent aux enregistrements DB.
|
||
|
|
|
||
|
|
**Impact** : Incohérences non détectées.
|
||
|
|
|
||
|
|
**Recommandation** : Job de vérification d'intégrité quotidien.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. RÉSUMÉ DES RISQUES
|
||
|
|
|
||
|
|
### 🔴 P0 — Must-Fix avant déploiement
|
||
|
|
|
||
|
|
1. **Backend Go** : Erreurs JSON non traitées silencieusement
|
||
|
|
2. **Backend Go** : Absence de transactions dans opérations critiques
|
||
|
|
3. **Chat Server** : Race condition dans TypingIndicatorManager
|
||
|
|
4. **Chat Server** : Panics possibles (31 fichiers avec `unwrap()`)
|
||
|
|
5. **Stream Server** : Tasks non cancellées proprement
|
||
|
|
6. **Global** : Pas de transaction distribuée
|
||
|
|
7. **Global** : Manque de tests unitaires critiques
|
||
|
|
8. **Global** : Jobs/messages/segments peuvent être incohérents
|
||
|
|
|
||
|
|
### 🟠 P1 — Production-grade minimal
|
||
|
|
|
||
|
|
1. **Backend Go** : Erreurs silencieuses, validation input incomplète
|
||
|
|
2. **Backend Go** : Race condition dans workers retries
|
||
|
|
3. **Backend Go** : Timing attack password reset
|
||
|
|
4. **Chat Server** : Race conditions dans DeliveredStatusManager/ReadReceiptManager
|
||
|
|
5. **Chat Server** : Pas de panic boundary dans WebSocket handler
|
||
|
|
6. **Chat Server** : Tasks orphelins, pas de heartbeat timeout
|
||
|
|
7. **Stream Server** : Erreurs FFmpeg non propagées, DB pas toujours sync
|
||
|
|
8. **Stream Server** : Corruption d'état concurrent dans SegmentTracker
|
||
|
|
9. **Stream Server** : Regex non robustes, IO errors incomplètes
|
||
|
|
10. **Global** : Pas de validation croisée IDs, pas de tests de charge
|
||
|
|
11. **Global** : Fuites goroutine/task, pas de correlation-id
|
||
|
|
|
||
|
|
### 🟡 P2 — Qualité continue
|
||
|
|
|
||
|
|
1. **Backend Go** : Pas de circuit breaker health check
|
||
|
|
2. **Backend Go** : Queue in-memory sans persistance
|
||
|
|
3. **Global** : Logs non structurés, pas de vérification d'intégrité
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 📊 STATISTIQUES
|
||
|
|
|
||
|
|
- **P0 (Critique)** : 8 problèmes
|
||
|
|
- **P1 (Important)** : 11 problèmes
|
||
|
|
- **P2 (Amélioration)** : 3 problèmes
|
||
|
|
- **Total** : 22 problèmes identifiés
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 🔗 LIENS AVEC TRIAGE ACTUEL
|
||
|
|
|
||
|
|
Voir `TRIAGE.md` pour l'état fonctionnel des features. Cet audit se concentre sur la **robustesse** et la **stabilité**, pas sur les features manquantes.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Prochaines étapes** : Générer `HARDENING_PLAN.md` avec plan de correction priorisé.
|
||
|
|
|