637 lines
25 KiB
Markdown
637 lines
25 KiB
Markdown
|
|
# 🔍 AUDIT EXHAUSTIF - VEZA CHAT SERVER
|
||
|
|
## Module: `veza-chat-server` (Rust)
|
||
|
|
|
||
|
|
**Date**: 2025-01-27
|
||
|
|
**Auditeur**: Auto (Cursor AI)
|
||
|
|
**Version analysée**: 0.2.0
|
||
|
|
**Statut compilation**: ❌ **ÉCHEC** (conflit de dépendances SQLx)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE A — CARTOGRAPHIE DU MODULE
|
||
|
|
|
||
|
|
## 1. But du module
|
||
|
|
|
||
|
|
**Rôle**: Serveur de chat temps réel avec WebSocket pour la plateforme Veza.
|
||
|
|
|
||
|
|
**Fonctionnalités principales**:
|
||
|
|
- Communication WebSocket bidirectionnelle (Axum + tokio-tungstenite)
|
||
|
|
- Gestion de conversations (directes, groupes, channels)
|
||
|
|
- Messages avec édition/suppression
|
||
|
|
- Read receipts et delivered status
|
||
|
|
- Typing indicators
|
||
|
|
- Recherche et synchronisation d'historique
|
||
|
|
- Authentification JWT avec refresh tokens
|
||
|
|
- Permissions RBAC (admin, moderator, member)
|
||
|
|
- Event Bus RabbitMQ (optionnel)
|
||
|
|
- Métriques Prometheus
|
||
|
|
|
||
|
|
## 2. Entrées / Sorties
|
||
|
|
|
||
|
|
### APIs exposées
|
||
|
|
|
||
|
|
**HTTP REST** (port 8081 par défaut):
|
||
|
|
- `GET /health` - Health check
|
||
|
|
- `GET /healthz` - Health check (alias)
|
||
|
|
- `GET /readyz` - Readiness check (DB + RabbitMQ)
|
||
|
|
- `GET /metrics` - Métriques Prometheus
|
||
|
|
- `GET /api/messages/stats` - Statistiques serveur
|
||
|
|
- `GET /api/messages/{conversation_id}` - Récupération messages (authentifié)
|
||
|
|
- `POST /api/messages` - Envoi message (authentifié)
|
||
|
|
|
||
|
|
**WebSocket** (port 8081):
|
||
|
|
- `GET /ws?token=<JWT>` - Connexion WebSocket
|
||
|
|
|
||
|
|
**Formats**:
|
||
|
|
- JSON pour HTTP REST
|
||
|
|
- JSON pour WebSocket (messages structurés)
|
||
|
|
- Protobuf pour gRPC (présent mais non utilisé dans main.rs)
|
||
|
|
|
||
|
|
### Events WebSocket
|
||
|
|
|
||
|
|
**Incoming** (`IncomingMessage`):
|
||
|
|
- `SendMessage`, `JoinConversation`, `LeaveConversation`
|
||
|
|
- `MarkAsRead`, `Typing`, `Delivered`
|
||
|
|
- `EditMessage`, `DeleteMessage`
|
||
|
|
- `FetchHistory`, `SearchMessages`, `SyncMessages`
|
||
|
|
- `Ping`
|
||
|
|
|
||
|
|
**Outgoing** (`OutgoingMessage`):
|
||
|
|
- `NewMessage`, `MessageRead`, `MessageDelivered`
|
||
|
|
- `UserTyping`, `MessageEdited`, `MessageDeleted`
|
||
|
|
- `HistoryChunk`, `SearchResults`, `SyncChunk`
|
||
|
|
- `ActionConfirmed`, `Error`, `Pong`
|
||
|
|
|
||
|
|
## 3. Dépendances internes
|
||
|
|
|
||
|
|
- `veza-common` (path: `../veza-common`) - Types partagés
|
||
|
|
- Modules internes: `config`, `database`, `error`, `jwt_manager`, `repository`, `security`, `services`, `websocket`
|
||
|
|
|
||
|
|
## 4. Dépendances externes
|
||
|
|
|
||
|
|
**Base de données**:
|
||
|
|
- PostgreSQL (via SQLx 0.8.6)
|
||
|
|
- Migrations SQL dans `migrations/`
|
||
|
|
|
||
|
|
**Cache** (optionnel):
|
||
|
|
- Redis (via `redis` crate, feature `redis-cache`)
|
||
|
|
|
||
|
|
**Message Broker** (optionnel):
|
||
|
|
- RabbitMQ (via `lapin` 2.3)
|
||
|
|
|
||
|
|
**Monitoring**:
|
||
|
|
- Prometheus (via `metrics-exporter-prometheus`)
|
||
|
|
|
||
|
|
## 5. Exécution
|
||
|
|
|
||
|
|
### Build
|
||
|
|
```bash
|
||
|
|
cargo build --release
|
||
|
|
```
|
||
|
|
|
||
|
|
### Run
|
||
|
|
```bash
|
||
|
|
./target/release/chat-server
|
||
|
|
```
|
||
|
|
|
||
|
|
### Variables d'environnement critiques
|
||
|
|
- `DATABASE_URL` (requis) - PostgreSQL connection string
|
||
|
|
- `JWT_SECRET` (requis, min 32 chars) - Secret pour JWT
|
||
|
|
- `CHAT_SERVER_PORT` (défaut: 8081)
|
||
|
|
- `CHAT_SERVER_HOST` (défaut: 0.0.0.0)
|
||
|
|
- `RABBITMQ_URL` (optionnel)
|
||
|
|
- `RABBITMQ_ENABLE` (défaut: true)
|
||
|
|
|
||
|
|
### Docker
|
||
|
|
- `Dockerfile` présent (multi-stage, Alpine)
|
||
|
|
- `docker-compose.yml` présent
|
||
|
|
- Healthcheck configuré (`/health`)
|
||
|
|
|
||
|
|
## 6. Points d'intégration
|
||
|
|
|
||
|
|
**Backend Go**:
|
||
|
|
- JWT tokens partagés (audience: `veza-chat`, issuer: `veza-backend`)
|
||
|
|
- Schéma DB partagé (UUID pour users, conversations, messages)
|
||
|
|
|
||
|
|
**Frontend React**:
|
||
|
|
- WebSocket: `ws://<host>:8081/ws?token=<JWT>`
|
||
|
|
- REST API: `http://<host>:8081/api/*`
|
||
|
|
- Headers: `Authorization: Bearer <JWT>`
|
||
|
|
|
||
|
|
**Auth**:
|
||
|
|
- JWT HS256 (configurable)
|
||
|
|
- Claims: `sub` (user_id UUID), `username`, `role`, `aud`, `iss`, `exp`, `iat`, `jti`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE B — SANTÉ TECHNIQUE
|
||
|
|
|
||
|
|
## Build Status
|
||
|
|
|
||
|
|
### ❌ **P0 - BUILD CASSÉ**
|
||
|
|
|
||
|
|
**Erreur**: Conflit de dépendances SQLx
|
||
|
|
|
||
|
|
```
|
||
|
|
error: failed to select a version for `libsqlite3-sys`.
|
||
|
|
package `libsqlite3-sys v0.30.1` (sqlx 0.8.6)
|
||
|
|
conflicts with `libsqlite3-sys v0.26.0` (sqlx 0.7.0 via veza-common)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fichiers concernés**:
|
||
|
|
- `Cargo.toml` ligne 43: `sqlx = "0.8.6"`
|
||
|
|
- `veza-common` (externe): `sqlx = "^0.7"`
|
||
|
|
|
||
|
|
**Impact**: **Impossible de compiler le projet**
|
||
|
|
|
||
|
|
**Fix minimal**: Aligner les versions SQLx entre `chat_server` et `veza-common`, ou exclure SQLite de `veza-common` si non utilisé.
|
||
|
|
|
||
|
|
## Tests
|
||
|
|
|
||
|
|
### Couverture
|
||
|
|
- Tests unitaires présents dans plusieurs modules
|
||
|
|
- Tests d'intégration avec `#[ignore]` (nécessitent DB)
|
||
|
|
- Tests JWT présents (`jwt_manager.rs`)
|
||
|
|
- Tests permissions partiels
|
||
|
|
|
||
|
|
### Problèmes détectés
|
||
|
|
- Beaucoup de tests ignorés (`#[ignore]`) car nécessitent DB
|
||
|
|
- Pas de tests E2E WebSocket
|
||
|
|
- Tests de sécurité manquants (injection, rate limiting)
|
||
|
|
|
||
|
|
## Gestion des erreurs
|
||
|
|
|
||
|
|
### ✅ Points positifs
|
||
|
|
- Type `ChatError` exhaustif avec `thiserror`
|
||
|
|
- Helpers pour création d'erreurs (`ChatError::not_found`, etc.)
|
||
|
|
- Mapping HTTP status codes approprié
|
||
|
|
- Logging structuré avec `tracing`
|
||
|
|
|
||
|
|
### ⚠️ Points d'attention
|
||
|
|
- **169 occurrences de `unwrap()` / `expect()` / `panic!`** détectées
|
||
|
|
- Certains `unwrap()` dans code de production (ex: `src/config.rs:21`, `src/main.rs:489`)
|
||
|
|
- Panics possibles dans `SecurityConfig::default()` (ligne 192) si appelé hors test
|
||
|
|
|
||
|
|
## Linters / Qualité
|
||
|
|
|
||
|
|
### Clippy
|
||
|
|
- Non exécuté dans l'audit (nécessite build)
|
||
|
|
- Recommandation: `cargo clippy --all-targets --all-features -- -D warnings`
|
||
|
|
|
||
|
|
### Conventions
|
||
|
|
- ✅ Structure modulaire claire
|
||
|
|
- ✅ Documentation rustdoc présente
|
||
|
|
- ⚠️ Mix de noms français/anglais (ex: `conversation_id` vs `room_id`)
|
||
|
|
- ⚠️ Code mort potentiel (`security_legacy.rs`, `simple_message_store.rs`)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE C — SÉCURITÉ
|
||
|
|
|
||
|
|
## Top 10 Risques Critiques
|
||
|
|
|
||
|
|
### **P0-001: Conflit de dépendances SQLx (Build cassé)**
|
||
|
|
- **Impact**: Impossible de déployer
|
||
|
|
- **Fichier**: `Cargo.toml:43` vs `veza-common`
|
||
|
|
- **Fix**: Aligner versions ou exclure SQLite
|
||
|
|
- **Effort**: S (1h)
|
||
|
|
|
||
|
|
### **P0-002: JWT Secret faible par défaut**
|
||
|
|
- **Impact**: Tokens compromis si secret faible
|
||
|
|
- **Fichier**: `src/main.rs:158`, `env.example:20`
|
||
|
|
- **Preuve**: `JWT_SECRET=your-super-secret-jwt-key-change-this-in-production`
|
||
|
|
- **Fix**: Validation stricte min 64 chars, génération aléatoire au démarrage si absent
|
||
|
|
- **Effort**: S (30min)
|
||
|
|
|
||
|
|
### **P0-003: Panics dans code de production**
|
||
|
|
- **Impact**: Crash serveur
|
||
|
|
- **Fichiers**:
|
||
|
|
- `src/main.rs:489` - `expect("failed to install Ctrl+C handler")`
|
||
|
|
- `src/config.rs:192` - `panic!` dans `SecurityConfig::default()`
|
||
|
|
- `src/env.rs:30,61` - `panic!` dans helpers
|
||
|
|
- **Fix**: Remplacer par `Result` et gestion d'erreurs
|
||
|
|
- **Effort**: M (2h)
|
||
|
|
|
||
|
|
### **P0-004: Validation JWT incomplète**
|
||
|
|
- **Impact**: Tokens expirés ou invalides acceptés
|
||
|
|
- **Fichier**: `src/jwt_manager.rs:266-303`
|
||
|
|
- **Preuve**: Vérification `exp` manuelle après décodage (ligne 290-293), mais pas de vérification `nbf` (not before)
|
||
|
|
- **Fix**: Ajouter validation `nbf`, vérifier `iss`/`aud` strictement
|
||
|
|
- **Effort**: S (1h)
|
||
|
|
|
||
|
|
### **P1-005: SQL Injection potentielle dans recherche**
|
||
|
|
- **Impact**: Exécution de requêtes SQL arbitraires
|
||
|
|
- **Fichier**: `src/repository/message_repository.rs:563`
|
||
|
|
- **Preuve**: `format!("%{}%", query)` - si `query` contient `%` ou `_`, comportement inattendu
|
||
|
|
- **Fix**: Échapper caractères spéciaux ou utiliser `ILIKE` avec paramètre bindé
|
||
|
|
- **Effort**: S (30min)
|
||
|
|
|
||
|
|
### **P1-006: Rate limiting non implémenté**
|
||
|
|
- **Impact**: DoS possible, spam
|
||
|
|
- **Fichier**: `src/security/mod.rs:94` - TODO comment
|
||
|
|
- **Preuve**: `// TODO: Implémenter le Rate Limiting réel via Redis ou mémoire partagée`
|
||
|
|
- **Fix**: Implémenter rate limiter avec Redis ou in-memory (DashMap)
|
||
|
|
- **Effort**: M (4h)
|
||
|
|
|
||
|
|
### **P1-007: CORS non configuré**
|
||
|
|
- **Impact**: XSS via requêtes cross-origin
|
||
|
|
- **Fichier**: `src/main.rs` - Pas de middleware CORS
|
||
|
|
- **Preuve**: Aucune configuration CORS dans Axum router
|
||
|
|
- **Fix**: Ajouter middleware CORS avec origines whitelistées
|
||
|
|
- **Effort**: S (1h)
|
||
|
|
|
||
|
|
### **P1-008: Secrets dans logs**
|
||
|
|
- **Impact**: Fuite de secrets en production
|
||
|
|
- **Fichier**: `src/config.rs:55` - Logging de `database_url` potentiellement
|
||
|
|
- **Preuve**: `info!("Initializing database connection pool with config: {:?}", config)` - peut logger credentials
|
||
|
|
- **Fix**: Masquer credentials dans logs (remplacer par `***`)
|
||
|
|
- **Effort**: S (30min)
|
||
|
|
|
||
|
|
### **P1-009: WebSocket sans rate limiting**
|
||
|
|
- **Impact**: Spam de messages, DoS
|
||
|
|
- **Fichier**: `src/websocket/handler.rs:200-894`
|
||
|
|
- **Preuve**: Aucune limite sur fréquence de messages WebSocket
|
||
|
|
- **Fix**: Ajouter rate limiter par client (ex: max 100 messages/sec)
|
||
|
|
- **Effort**: M (3h)
|
||
|
|
|
||
|
|
### **P1-010: Blacklist JWT en mémoire (non persistant)**
|
||
|
|
- **Impact**: Tokens révoqués revalidés après redémarrage
|
||
|
|
- **Fichier**: `src/jwt_manager.rs:142`
|
||
|
|
- **Preuve**: `revoked_tokens: Arc<RwLock<HashSet<String>>>` - perdu au redémarrage
|
||
|
|
- **Fix**: Persister blacklist dans Redis ou DB
|
||
|
|
- **Effort**: M (2h)
|
||
|
|
|
||
|
|
## Autres Risques (P2/P3)
|
||
|
|
|
||
|
|
### P2-011: Validation de contenu basique
|
||
|
|
- **Fichier**: `src/security_legacy.rs` - Regex patterns, mais module "legacy"
|
||
|
|
- **Fix**: Utiliser `ammonia` (déjà dans deps) pour sanitization HTML
|
||
|
|
|
||
|
|
### P2-012: Pas de protection CSRF pour REST API
|
||
|
|
- **Fichier**: `src/security/csrf.rs` existe mais non utilisé dans `main.rs`
|
||
|
|
- **Fix**: Activer middleware CSRF pour routes POST/PUT/DELETE
|
||
|
|
|
||
|
|
### P2-013: Heartbeat WebSocket fixe (60s)
|
||
|
|
- **Fichier**: `src/websocket/handler.rs:121`
|
||
|
|
- **Preuve**: `keepalive_timeout = Duration::from_secs(60)` - hardcodé
|
||
|
|
- **Fix**: Configurable via env var
|
||
|
|
|
||
|
|
### P2-014: Pas de validation de taille de message
|
||
|
|
- **Fichier**: `src/websocket/handler.rs:214` - Pas de check `content.len()`
|
||
|
|
- **Fix**: Valider `MAX_MESSAGE_LENGTH` (défini dans `env.example:57` mais non utilisé)
|
||
|
|
|
||
|
|
### P3-015: Logs verbeux en production
|
||
|
|
- **Fichier**: `src/main.rs:84-101` - Logs avec `debug`/`info` même en prod
|
||
|
|
- **Fix**: Utiliser `RUST_LOG` avec niveaux appropriés
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE D — ROBUSTESSE & OBSERVABILITÉ
|
||
|
|
|
||
|
|
## Logs structurés
|
||
|
|
|
||
|
|
### ✅ Points positifs
|
||
|
|
- `tracing` avec `tracing-subscriber` configuré
|
||
|
|
- Format JSON en production (`main.rs:92`)
|
||
|
|
- Format détaillé en dev (`main.rs:95-100`)
|
||
|
|
- Champs structurés (`user_id = %user_id`, `conversation_id = %conversation_id`)
|
||
|
|
|
||
|
|
### ⚠️ Gaps
|
||
|
|
- **Pas de `request_id` / `trace_id`** pour corrélation
|
||
|
|
- **Pas de log rotation** configuré (mentionné dans `env.example:83` mais non implémenté)
|
||
|
|
- **Secrets potentiellement loggés** (voir P1-008)
|
||
|
|
|
||
|
|
## Métriques
|
||
|
|
|
||
|
|
### ✅ Présent
|
||
|
|
- Prometheus exporter (`/metrics`)
|
||
|
|
- `ChatMetrics` avec compteurs/gauges
|
||
|
|
- Métriques système (CPU, mémoire) via `sysinfo`
|
||
|
|
|
||
|
|
### ⚠️ Manquants
|
||
|
|
- **Latence P50/P95/P99** pour requêtes DB
|
||
|
|
- **Taux d'erreur par endpoint**
|
||
|
|
- **Connexions WebSocket actives** (compteur)
|
||
|
|
- **Taille de la blacklist JWT**
|
||
|
|
|
||
|
|
## Healthchecks
|
||
|
|
|
||
|
|
### ✅ Présent
|
||
|
|
- `/health` - Basic health check
|
||
|
|
- `/readyz` - Readiness (DB + RabbitMQ)
|
||
|
|
|
||
|
|
### ⚠️ Améliorations
|
||
|
|
- **Liveness check** séparé (actuellement `/health` fait DB check aussi)
|
||
|
|
- **Timeout configurable** pour healthchecks
|
||
|
|
- **Circuit breaker** pour DB/RabbitMQ
|
||
|
|
|
||
|
|
## Timeouts & Retries
|
||
|
|
|
||
|
|
### ✅ Présent
|
||
|
|
- Timeout WebSocket inactivité (60s)
|
||
|
|
- Retry RabbitMQ (`max_retries`, `retry_interval_secs`)
|
||
|
|
|
||
|
|
### ⚠️ Manquants
|
||
|
|
- **Timeout pour requêtes DB** (seulement `acquire_timeout` dans pool)
|
||
|
|
- **Retry avec backoff exponentiel** pour DB
|
||
|
|
- **Circuit breaker** pour services externes
|
||
|
|
|
||
|
|
## Gestion de charge
|
||
|
|
|
||
|
|
### ✅ Présent
|
||
|
|
- Pool DB configuré (max 20, min 5)
|
||
|
|
- Limite messages (100 max dans `fetch_history`)
|
||
|
|
|
||
|
|
### ⚠️ Gaps
|
||
|
|
- **Pas de backpressure** pour WebSocket (clients peuvent spam)
|
||
|
|
- **Pas de limite de connexions WebSocket simultanées**
|
||
|
|
- **Pas de queue pour messages en attente**
|
||
|
|
|
||
|
|
## Migrations
|
||
|
|
|
||
|
|
### ✅ Présent
|
||
|
|
- Migrations SQL dans `migrations/`
|
||
|
|
- SQLx migrate supporté
|
||
|
|
|
||
|
|
### ⚠️ Problèmes
|
||
|
|
- **16 fichiers de migration** - risque de confusion
|
||
|
|
- **Migrations archivées** dans `migrations/archive/` - à nettoyer
|
||
|
|
- **Pas de rollback** automatique en cas d'échec
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE E — PERFORMANCE & SCALABILITÉ
|
||
|
|
|
||
|
|
## Hotspots identifiés
|
||
|
|
|
||
|
|
### 1. Broadcast WebSocket inefficace
|
||
|
|
**Fichier**: `src/websocket/mod.rs:228-244`
|
||
|
|
**Problème**: Itération sur tous les clients pour chaque broadcast
|
||
|
|
```rust
|
||
|
|
for client in clients.iter() {
|
||
|
|
let conversations = client.conversations.read().await;
|
||
|
|
if conversations.contains(&conversation_id) {
|
||
|
|
let _ = client.send_message(message.clone()).await;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
**Impact**: O(n) où n = nombre total de clients, même si seulement quelques-uns sont dans la conversation
|
||
|
|
**Fix**: Index inversé `conversation_id -> Vec<client_id>` pour O(1) lookup
|
||
|
|
**Effort**: M (3h)
|
||
|
|
|
||
|
|
### 2. Clonage de messages pour broadcast
|
||
|
|
**Fichier**: `src/websocket/mod.rs:239`
|
||
|
|
**Problème**: `message.clone()` pour chaque client
|
||
|
|
**Impact**: Allocations inutiles pour gros messages
|
||
|
|
**Fix**: Utiliser `Arc<OutgoingMessage>` ou sérialiser une fois, cloner bytes
|
||
|
|
**Effort**: S (1h)
|
||
|
|
|
||
|
|
### 3. Requêtes DB N+1 potentielles
|
||
|
|
**Fichier**: `src/repository/message_repository.rs:82-144`
|
||
|
|
**Problème**: Pas de batch loading pour conversations multiples
|
||
|
|
**Impact**: Latence élevée si plusieurs conversations chargées
|
||
|
|
**Fix**: Ajouter méthode `get_multiple_conversations_messages`
|
||
|
|
**Effort**: M (2h)
|
||
|
|
|
||
|
|
### 4. Blacklist JWT en mémoire (HashSet)
|
||
|
|
**Fichier**: `src/jwt_manager.rs:142`
|
||
|
|
**Problème**: `HashSet<String>` - recherche O(1) mais mémoire illimitée
|
||
|
|
**Impact**: Fuite mémoire si beaucoup de tokens révoqués
|
||
|
|
**Fix**: LRU cache ou TTL-based cleanup (déjà partiellement implémenté ligne 473)
|
||
|
|
**Effort**: S (1h)
|
||
|
|
|
||
|
|
### 5. Parsing JSON répété
|
||
|
|
**Fichier**: `src/websocket/handler.rs:210`
|
||
|
|
**Problème**: `serde_json::from_str(text)` à chaque message
|
||
|
|
**Impact**: CPU overhead pour gros payloads
|
||
|
|
**Fix**: Cache de schémas JSON ou validation pré-compilée
|
||
|
|
**Effort**: P3 (optimisation future)
|
||
|
|
|
||
|
|
## Streaming & I/O
|
||
|
|
|
||
|
|
### WebSocket
|
||
|
|
- ✅ Utilisation de `tokio-tungstenite` (async)
|
||
|
|
- ⚠️ Pas de compression WebSocket (per-message deflate)
|
||
|
|
- ⚠️ Pas de fragmentation pour gros messages
|
||
|
|
|
||
|
|
### Base de données
|
||
|
|
- ✅ Pool de connexions configuré
|
||
|
|
- ⚠️ Pas de prepared statements caching explicite (SQLx le fait mais non configuré)
|
||
|
|
- ⚠️ Pas de connection pooling metrics exposées
|
||
|
|
|
||
|
|
## Async Runtime
|
||
|
|
|
||
|
|
### ✅ Points positifs
|
||
|
|
- Tokio avec features "full"
|
||
|
|
- Pas de blocking dans async (sauf `sysinfo` potentiellement)
|
||
|
|
|
||
|
|
### ⚠️ Points d'attention
|
||
|
|
- **Pas de configuration de worker threads** (utilise par défaut)
|
||
|
|
- **Pas de metrics Tokio** (task spawns, park/unpark)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE F — LISTE EXHAUSTIVE DES PROBLÈMES
|
||
|
|
|
||
|
|
## P0 - CRITIQUES (Build / Sécurité / Crash)
|
||
|
|
|
||
|
|
| ID | Titre | Impact | Fichier | Fix minimal | Validation | Effort |
|
||
|
|
|---|---|---|---|---|---|---|
|
||
|
|
| **MOD-P0-001** | Conflit dépendances SQLx (build cassé) | Impossible de compiler | `Cargo.toml:43` | Aligner versions SQLx (0.8.6 partout) ou exclure SQLite de veza-common | `cargo check` passe | S (1h) |
|
||
|
|
| **MOD-P0-002** | JWT Secret faible par défaut | Tokens compromis | `src/main.rs:158`, `env.example:20` | Validation min 64 chars, génération aléatoire si absent | Test: secret < 64 chars rejeté | S (30min) |
|
||
|
|
| **MOD-P0-003** | Panics dans code production | Crash serveur | `src/main.rs:489`, `src/config.rs:192`, `src/env.rs:30,61` | Remplacer `expect/panic` par `Result` | Tests: pas de panic sur erreurs attendues | M (2h) |
|
||
|
|
| **MOD-P0-004** | Validation JWT incomplète | Tokens invalides acceptés | `src/jwt_manager.rs:266-303` | Ajouter validation `nbf`, vérifier `iss/aud` strictement | Test: token avec `nbf` futur rejeté | S (1h) |
|
||
|
|
|
||
|
|
## P1 - HAUTE PRIORITÉ (Bugs fréquents / Dette bloquante)
|
||
|
|
|
||
|
|
| ID | Titre | Impact | Fichier | Fix minimal | Validation | Effort |
|
||
|
|
|---|---|---|---|---|---|---|
|
||
|
|
| **MOD-P1-005** | SQL Injection potentielle recherche | Exécution SQL arbitraire | `src/repository/message_repository.rs:563` | Échapper `%` et `_` ou utiliser paramètre bindé | Test: query avec `%` ne match pas littéralement | S (30min) |
|
||
|
|
| **MOD-P1-006** | Rate limiting non implémenté | DoS, spam | `src/security/mod.rs:94` | Implémenter avec Redis ou DashMap | Test: 1000 req/sec rejetées | M (4h) |
|
||
|
|
| **MOD-P1-007** | CORS non configuré | XSS cross-origin | `src/main.rs:246` | Ajouter middleware CORS Axum | Test: requête cross-origin rejetée sans header | S (1h) |
|
||
|
|
| **MOD-P1-008** | Secrets dans logs | Fuite credentials | `src/config.rs:264` | Masquer credentials (remplacer par `***`) | Test: logs ne contiennent pas `password=` | S (30min) |
|
||
|
|
| **MOD-P1-009** | WebSocket sans rate limiting | Spam messages, DoS | `src/websocket/handler.rs:200` | Rate limiter par client (100 msg/sec) | Test: client spammant rejeté | M (3h) |
|
||
|
|
| **MOD-P1-010** | Blacklist JWT non persistant | Tokens révoqués revalidés | `src/jwt_manager.rs:142` | Persister dans Redis ou DB | Test: token révoqué reste révoqué après restart | M (2h) |
|
||
|
|
| **MOD-P1-011** | Pas de validation taille message | Messages trop longs | `src/websocket/handler.rs:214` | Valider `MAX_MESSAGE_LENGTH` (2000 chars) | Test: message > 2000 chars rejeté | S (30min) |
|
||
|
|
| **MOD-P1-012** | Broadcast WebSocket O(n) | Performance dégradée | `src/websocket/mod.rs:228` | Index inversé conversation -> clients | Test: broadcast à 1000 clients < 10ms | M (3h) |
|
||
|
|
| **MOD-P1-013** | Pas de limite connexions WS | DoS par connexions | `src/websocket/manager.rs:209` | Limiter max connexions (ex: 10000) | Test: 10001ème connexion rejetée | S (1h) |
|
||
|
|
| **MOD-P1-014** | Healthcheck timeout non configuré | Healthcheck bloque | `src/main.rs:298` | Timeout configurable (ex: 5s) | Test: DB lent retourne 503 | S (30min) |
|
||
|
|
|
||
|
|
## P2 - MOYENNE PRIORITÉ (Qualité / Maintenabilité)
|
||
|
|
|
||
|
|
| ID | Titre | Impact | Fichier | Fix minimal | Validation | Effort |
|
||
|
|
|---|---|---|---|---|---|---|
|
||
|
|
| **MOD-P2-015** | Pas de request_id/trace_id | Debug difficile | `src/main.rs:82` | Ajouter middleware tracing avec `request_id` | Test: logs contiennent `request_id` | M (2h) |
|
||
|
|
| **MOD-P2-016** | Log rotation non implémenté | Disque plein | `env.example:83` | Implémenter avec `tracing-appender` | Test: logs rotent après 100MB | M (2h) |
|
||
|
|
| **MOD-P2-017** | Métriques latence manquantes | Monitoring incomplet | `src/monitoring.rs` | Ajouter histogrammes P50/P95/P99 | Test: métriques exposées sur `/metrics` | M (2h) |
|
||
|
|
| **MOD-P2-018** | Circuit breaker manquant | Cascading failures | N/A | Implémenter avec `tower` ou custom | Test: DB down -> circuit ouvert | M (4h) |
|
||
|
|
| **MOD-P2-019** | Migrations multiples confuses | Risque d'erreur | `migrations/` (16 fichiers) | Nettoyer migrations archivées | Test: migrations appliquent dans ordre | S (1h) |
|
||
|
|
| **MOD-P2-020** | Code mort (security_legacy) | Maintenance inutile | `src/security_legacy.rs` | Supprimer ou documenter usage | Test: build sans ce fichier | S (30min) |
|
||
|
|
| **MOD-P2-021** | CSRF non activé | CSRF attacks | `src/security/csrf.rs` | Activer middleware CSRF | Test: requête sans token CSRF rejetée | M (2h) |
|
||
|
|
| **MOD-P2-022** | Heartbeat WebSocket hardcodé | Non configurable | `src/websocket/handler.rs:121` | Configurable via env var | Test: heartbeat = 30s fonctionne | S (30min) |
|
||
|
|
| **MOD-P2-023** | Clonage messages broadcast | Allocations inutiles | `src/websocket/mod.rs:239` | Utiliser `Arc<OutgoingMessage>` | Test: broadcast 100 clients < 5ms | S (1h) |
|
||
|
|
| **MOD-P2-024** | Requêtes N+1 potentielles | Latence élevée | `src/repository/message_repository.rs` | Batch loading conversations | Test: 10 conversations < 100ms | M (2h) |
|
||
|
|
|
||
|
|
## P3 - BASSE PRIORITÉ (Cosmétique / Refactors)
|
||
|
|
|
||
|
|
| ID | Titre | Impact | Fichier | Fix minimal | Validation | Effort |
|
||
|
|
|---|---|---|---|---|---|---|
|
||
|
|
| **MOD-P3-025** | Mix français/anglais | Confusion | Multiple | Standardiser sur anglais | Review code | L (8h) |
|
||
|
|
| **MOD-P3-026** | Logs verbeux en production | Bruit | `src/main.rs:84` | Utiliser `RUST_LOG` approprié | Test: prod logs = info seulement | S (30min) |
|
||
|
|
| **MOD-P3-027** | Pas de compression WebSocket | Bande passante | `src/websocket/handler.rs:47` | Activer per-message deflate | Test: messages compressés | M (2h) |
|
||
|
|
| **MOD-P3-028** | Tests ignorés nombreux | Couverture faible | Multiple `#[ignore]` | Setup DB de test ou mocks | Test: tous tests passent | L (8h) |
|
||
|
|
| **MOD-P3-029** | Documentation rustdoc incomplète | DX | Multiple | Compléter doc comments | Test: `cargo doc` sans warnings | M (4h) |
|
||
|
|
| **MOD-P3-030** | Pas de benchmarks | Performance non mesurée | N/A | Ajouter `criterion` benchmarks | Test: benchmarks passent | M (4h) |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# PHASE G — PLAN D'EXÉCUTION
|
||
|
|
|
||
|
|
## Checklist P0 (Ordre strict)
|
||
|
|
|
||
|
|
1. ✅ **MOD-P0-001**: Fix conflit SQLx
|
||
|
|
- Modifier `veza-common/Cargo.toml` pour utiliser `sqlx = "0.8.6"` OU exclure SQLite
|
||
|
|
- Vérifier: `cargo check` passe
|
||
|
|
- **PR**: `fix: align sqlx versions to 0.8.6`
|
||
|
|
|
||
|
|
2. ✅ **MOD-P0-002**: Validation JWT Secret
|
||
|
|
- Modifier `src/env.rs` pour valider min 64 chars
|
||
|
|
- Générer secret aléatoire si absent (dev seulement)
|
||
|
|
- **PR**: `security: enforce strong JWT secret (min 64 chars)`
|
||
|
|
|
||
|
|
3. ✅ **MOD-P0-003**: Remplacer panics
|
||
|
|
- `src/main.rs:489` -> `Result` pour signal handlers
|
||
|
|
- `src/config.rs:192` -> Supprimer `panic!` ou rendre test-only
|
||
|
|
- `src/env.rs:30,61` -> Retourner `Result` au lieu de `panic!`
|
||
|
|
- **PR**: `fix: replace panics with Result types`
|
||
|
|
|
||
|
|
4. ✅ **MOD-P0-004**: Validation JWT complète
|
||
|
|
- Ajouter validation `nbf` dans `validate_access_token`
|
||
|
|
- Vérifier `iss`/`aud` strictement (déjà fait partiellement)
|
||
|
|
- **PR**: `security: add nbf validation to JWT tokens`
|
||
|
|
|
||
|
|
## Checklist P1 (Par lots)
|
||
|
|
|
||
|
|
### Lot 1: Sécurité WebSocket (1 sprint)
|
||
|
|
- **MOD-P1-009**: Rate limiting WebSocket
|
||
|
|
- **MOD-P1-011**: Validation taille message
|
||
|
|
- **MOD-P1-013**: Limite connexions WS
|
||
|
|
- **PR**: `security: add rate limiting and validation for WebSocket`
|
||
|
|
|
||
|
|
### Lot 2: Sécurité REST (1 sprint)
|
||
|
|
- **MOD-P1-006**: Rate limiting REST
|
||
|
|
- **MOD-P1-007**: CORS middleware
|
||
|
|
- **MOD-P1-008**: Masquer secrets dans logs
|
||
|
|
- **PR**: `security: add rate limiting, CORS, and secure logging`
|
||
|
|
|
||
|
|
### Lot 3: Persistance & Robustesse (1 sprint)
|
||
|
|
- **MOD-P1-010**: Blacklist JWT persistant
|
||
|
|
- **MOD-P1-014**: Healthcheck timeout
|
||
|
|
- **MOD-P2-015**: Request ID tracing
|
||
|
|
- **PR**: `feat: persistent JWT blacklist, configurable healthcheck, request tracing`
|
||
|
|
|
||
|
|
### Lot 4: Performance WebSocket (1 sprint)
|
||
|
|
- **MOD-P1-012**: Index inversé broadcast
|
||
|
|
- **MOD-P2-023**: Arc pour messages
|
||
|
|
- **PR**: `perf: optimize WebSocket broadcast with index and Arc`
|
||
|
|
|
||
|
|
### Lot 5: Base de données (1 sprint)
|
||
|
|
- **MOD-P1-005**: Fix SQL injection recherche
|
||
|
|
- **MOD-P2-024**: Batch loading conversations
|
||
|
|
- **PR**: `fix: SQL injection in search, add batch loading`
|
||
|
|
|
||
|
|
## Quick Wins (≤ 1h chacun)
|
||
|
|
|
||
|
|
1. **MOD-P2-022**: Heartbeat configurable (30min)
|
||
|
|
2. **MOD-P2-020**: Supprimer code mort (30min)
|
||
|
|
3. **MOD-P3-026**: Logs production (30min)
|
||
|
|
4. **MOD-P2-019**: Nettoyer migrations (1h)
|
||
|
|
|
||
|
|
## Tests à ajouter en priorité
|
||
|
|
|
||
|
|
### Tests de sécurité
|
||
|
|
- [ ] Test: JWT secret < 64 chars rejeté
|
||
|
|
- [ ] Test: Token avec `nbf` futur rejeté
|
||
|
|
- [ ] Test: Recherche avec `%` ne match pas littéralement
|
||
|
|
- [ ] Test: Rate limiting (1000 req/sec rejetées)
|
||
|
|
- [ ] Test: CORS sans header rejeté
|
||
|
|
- [ ] Test: Message > 2000 chars rejeté
|
||
|
|
|
||
|
|
### Tests de robustesse
|
||
|
|
- [ ] Test: DB down -> healthcheck 503
|
||
|
|
- [ ] Test: Token révoqué reste révoqué après restart
|
||
|
|
- [ ] Test: Broadcast 1000 clients < 10ms
|
||
|
|
- [ ] Test: 10001ème connexion WS rejetée
|
||
|
|
|
||
|
|
### Tests E2E
|
||
|
|
- [ ] Test: Connexion WebSocket complète (join, send, leave)
|
||
|
|
- [ ] Test: Édition/suppression message
|
||
|
|
- [ ] Test: Read receipts et delivered status
|
||
|
|
- [ ] Test: Recherche et synchronisation
|
||
|
|
|
||
|
|
## PR Plan (Découpe proposée)
|
||
|
|
|
||
|
|
1. **`fix: resolve sqlx dependency conflict`** (P0-001)
|
||
|
|
2. **`security: enforce strong JWT secret and validation`** (P0-002, P0-004)
|
||
|
|
3. **`fix: replace panics with Result types`** (P0-003)
|
||
|
|
4. **`security: add rate limiting and validation for WebSocket`** (P1-009, P1-011, P1-013)
|
||
|
|
5. **`security: add rate limiting, CORS, and secure logging`** (P1-006, P1-007, P1-008)
|
||
|
|
6. **`feat: persistent JWT blacklist and request tracing`** (P1-010, P1-014, P2-015)
|
||
|
|
7. **`perf: optimize WebSocket broadcast`** (P1-012, P2-023)
|
||
|
|
8. **`fix: SQL injection and batch loading`** (P1-005, P2-024)
|
||
|
|
9. **`chore: cleanup migrations and dead code`** (P2-019, P2-020)
|
||
|
|
10. **`feat: add observability improvements`** (P2-016, P2-017, P2-018)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
# RÉSUMÉ EXÉCUTIF
|
||
|
|
|
||
|
|
## Statut global: 🔴 **NON PRODUCTION-READY**
|
||
|
|
|
||
|
|
### Bloqueurs (P0): 4
|
||
|
|
- Build cassé (conflit SQLx)
|
||
|
|
- Sécurité JWT faible
|
||
|
|
- Panics en production
|
||
|
|
- Validation JWT incomplète
|
||
|
|
|
||
|
|
### Critiques (P1): 10
|
||
|
|
- SQL injection potentielle
|
||
|
|
- Rate limiting manquant
|
||
|
|
- CORS non configuré
|
||
|
|
- Secrets dans logs
|
||
|
|
- Performance WebSocket
|
||
|
|
|
||
|
|
### Améliorations (P2): 10
|
||
|
|
- Observabilité incomplète
|
||
|
|
- Robustesse (circuit breakers, timeouts)
|
||
|
|
- Code mort
|
||
|
|
|
||
|
|
### Cosmétiques (P3): 6
|
||
|
|
- Documentation
|
||
|
|
- Tests
|
||
|
|
- Refactors
|
||
|
|
|
||
|
|
## Estimation totale
|
||
|
|
|
||
|
|
- **P0**: 4.5h (1 sprint)
|
||
|
|
- **P1**: 20h (2-3 sprints)
|
||
|
|
- **P2**: 18h (2 sprints)
|
||
|
|
- **P3**: 24h (3 sprints)
|
||
|
|
|
||
|
|
**Total**: ~66.5h (~8-9 jours de travail)
|
||
|
|
|
||
|
|
## Recommandation
|
||
|
|
|
||
|
|
**Priorité immédiate**: Fixer P0 (build + sécurité) avant toute autre chose.
|
||
|
|
|
||
|
|
**Roadmap suggérée**:
|
||
|
|
1. **Sprint 1**: P0 complet (build + sécurité critique)
|
||
|
|
2. **Sprint 2-3**: P1 sécurité (rate limiting, CORS, validation)
|
||
|
|
3. **Sprint 4-5**: P1 performance + P2 observabilité
|
||
|
|
4. **Sprint 6+**: P2 robustesse + P3 refactors
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Fin du rapport d'audit**
|
||
|
|
|