32 KiB
🔍 AUDIT TECHNIQUE EXHAUSTIF - CHAT SERVER RUST
Veza/Talas - Serveur de Chat Temps-Réel
Date: 2025-01-27
Version analysée: 0.2.0
Auditeur: Auto (AI Agent)
Durée audit: Exhaustif multi-phases
📋 SECTION 1 : RÉSUMÉ EXÉCUTIF
État Global du Projet
| Composant | État | Complétude | Notes |
|---|---|---|---|
| WebSocket Server | 🟡 Partiel | 65% | Framework Axum OK, auth JWT présente mais incomplète |
| Messages 1-to-1 | 🟢 Fonctionnel | 80% | Implémenté via conversations, persistence OK |
| Group Chat/Channels | 🟡 Partiel | 50% | Structure présente, permissions incomplètes |
| Typing Indicators | 🟢 Fonctionnel | 75% | Implémenté, timeout 3s, cleanup présent |
| Read Receipts | 🟡 Partiel | 40% | Structure DB présente, logique TODO ligne 258 |
| Presence | 🟢 Fonctionnel | 70% | Online/Away/Busy/Offline, multi-device partiel |
| Modération | 🟡 Partiel | 60% | Auto-modération basique, sanctions DB présentes |
| Performance | 🟡 À améliorer | 55% | Pas de benchmarks, optimisations manquantes |
| Sécurité | 🟠 Critique | 45% | Auth WebSocket OK, rate limiting présent, validation input partielle |
| Tests | 🔴 Insuffisant | 25% | Tests unitaires rares, integration tests ignorés |
| Documentation | 🟡 Partielle | 40% | Doc comments présents, exemples manquants |
Top 10 Problèmes Critiques
- 🔴 P0 - Auth WebSocket Token dans Query Params : Token JWT exposé dans URL (ligne 43
main.rs) - 🔴 P0 - Read Receipts Non Implémentés : TODO ligne 258
websocket/handler.rs - 🔴 P0 - Tests Coverage < 30% : Tests d'intégration ignorés, unitaires rares
- 🟠 P1 - Clippy Warnings Non Résolus : 10+ warnings compilation (unused imports, PartialEq manquant)
- 🟠 P1 - Rate Limiting Non Appliqué :
DosProtectionManagerprésent mais non utilisé dans handler - 🟠 P1 - Validation Input Messages : Pas de sanitization XSS, longueur max non vérifiée
- 🟠 P1 - Heartbeat/Ping-Pong Incomplet : Ping géré, mais pas de timeout idle connections
- 🟡 P2 - Modération Auto Basique : Détection spam simpliste (similarité Levenshtein)
- 🟡 P2 - Pas de Redis Pub/Sub : Broadcast limité à instance unique (pas de scaling horizontal)
- 🟡 P2 - Metrics Prometheus Non Utilisées : Métriques définies mais non enregistrées dans handlers
Effort Total Correction Estimé
- P0 (Bloquants): 3-5 jours
- P1 (Critiques): 5-7 jours
- P2 (Majeurs): 7-10 jours
- P3 (Mineurs): 3-5 jours
- TOTAL: 18-27 jours (3.5-5 semaines)
📊 SECTION 2 : CARTOGRAPHIE
2.1 Arborescence Rust
veza-chat-server/
├── src/
│ ├── main.rs ✅ Point d'entrée (Axum server)
│ ├── lib.rs ✅ Library root
│ ├── config.rs ✅ Configuration (env vars)
│ ├── error.rs ✅ Error types (thiserror)
│ ├── jwt_manager.rs ✅ JWT avec refresh tokens
│ ├── websocket/
│ │ ├── mod.rs ✅ Types messages WS
│ │ ├── handler.rs ⚠️ Handler WS (auth query param)
│ │ └── broadcast.rs ✅ Broadcast manager
│ ├── repository/
│ │ ├── message_repository.rs ✅ CRUD messages (SQLx)
│ │ └── room_repository.rs ✅ CRUD rooms
│ ├── models/
│ │ └── message.rs ✅ Modèle Message (UUID)
│ ├── services/
│ │ └── room_service.rs ✅ Service rooms
│ ├── auth.rs ⚠️ Auth manager (non utilisé)
│ ├── authentication.rs ⚠️ Auth alternatif (non utilisé)
│ ├── typing_indicator.rs ✅ Typing indicators
│ ├── read_receipts.rs ⚠️ Read receipts (i64 au lieu UUID)
│ ├── presence.rs ✅ Presence manager
│ ├── moderation.rs ⚠️ Modération (i32 user_id)
│ ├── rate_limiter.rs ⚠️ Rate limiter (non utilisé)
│ ├── prometheus_metrics.rs ⚠️ Metrics définies mais non utilisées
│ ├── event_bus.rs ✅ RabbitMQ EventBus
│ ├── database/
│ │ └── pool.rs ✅ Pool PostgreSQL
│ ├── security/
│ │ └── csrf.rs ✅ CSRF tokens
│ ├── core/ ⚠️ Modules non utilisés (duplication?)
│ └── hub/ ⚠️ Modules non utilisés (duplication?)
├── migrations/
│ ├── 001_create_clean_database.sql ✅ Schéma base
│ └── 002_advanced_features.sql ✅ Features avancées
├── tests/
│ └── integration_test.rs ⚠️ Tests ignorés (#[ignore])
├── Cargo.toml ✅ Dépendances complètes
└── env.example ✅ Configuration exemple
Problèmes détectés:
- Duplication:
auth.rsvsauthentication.rsvsjwt_manager.rs - Modules non utilisés:
core/,hub/(code mort?) - Incohérence types:
i32vsUUID(read_receipts, moderation)
2.2 Dépendances Cargo
| Catégorie | Dépendance | Version | État | Notes |
|---|---|---|---|---|
| Runtime | tokio | 1.35 | ✅ | Full features |
| WebSocket | axum | 0.8 | ✅ | Framework moderne |
| WebSocket | tokio-tungstenite | 0.21 | ✅ | Core WS |
| Database | sqlx | 0.8.6 | ✅ | PostgreSQL, macros |
| Cache | redis | 0.32 | ⚠️ | Optional, non utilisé |
| Auth | jsonwebtoken | 9.2 | ✅ | JWT |
| Serialization | serde | 1.0 | ✅ | JSON |
| Logging | tracing | 0.1 | ✅ | Structured logging |
| Metrics | metrics | 0.22 | ⚠️ | Optional, non utilisé |
| gRPC | tonic | 0.11 | ⚠️ | Présent mais non utilisé |
| Event Bus | lapin | 2.3 | ✅ | RabbitMQ |
| Validation | validator | 0.20 | ⚠️ | Présent mais non utilisé |
| Security | bcrypt | 0.17 | ⚠️ | Présent mais non utilisé |
Problèmes:
- Dépendances optionnelles non utilisées (redis, metrics, validator)
- gRPC présent mais serveur non démarré dans
main.rs - Versions à jour ✅
2.3 Configuration
Fichiers config:
env.example: ✅ Complet (131 lignes).env: Présent (non lu pour audit)config.rs: ✅ Chargement depuis env vars
Paramètres hardcodés:
main.rs:162: JWT secret par défaut hardcodé (fallback)typing_indicator.rs:19: Timeout 3s hardcodérate_limiter.rs:138: MAX_MESSAGES_PER_MINUTE = 60 hardcodé
Secrets management:
- ⚠️ JWT secret dans env var (pas de vault)
- ⚠️ Pas de rotation automatique secrets
⚡ SECTION 3 : WEBSOCKET & PROTOCOLE
3.1 Serveur WebSocket
Framework: Axum 0.8 avec ws feature ✅
Architecture:
- Single-threaded tokio runtime (par défaut)
- Connection pooling via
WebSocketManager(Vec<Arc>) - Broadcast via
broadcast.rs(tokio::sync::broadcast channels) - State:
Arc<RwLock<>>pour clients
Gestion connections:
- ✅ Handshake WebSocket via Axum
WebSocketUpgrade - ⚠️ PROBLÈME P0: Token JWT dans query params (ligne 43
handler.rs) - ✅ Ping/Pong implémenté (lignes 129-138
handler.rs) - ❌ Pas de timeout idle connections automatique
- ❌ Pas de heartbeat périodique (seulement ping client-initiated)
Code problématique:
// src/websocket/handler.rs:43
let token = match params.get("token") {
Some(t) => t,
None => {
error!("❌ Token manquant");
return (StatusCode::UNAUTHORIZED, "Missing token").into_response();
}
};
Impact: Token exposé dans logs serveur, URL, historique navigateur.
3.2 Protocole Messages
Format: JSON via serde_json ✅
Schéma messages:
// IncomingMessage (src/websocket/mod.rs:24)
pub enum IncomingMessage {
SendMessage { conversation_id, content, parent_message_id },
JoinConversation { conversation_id },
LeaveConversation { conversation_id },
MarkAsRead { conversation_id, message_id },
Ping,
}
// OutgoingMessage (src/websocket/mod.rs:47)
pub enum OutgoingMessage {
NewMessage { conversation_id, message_id, sender_id, content, created_at },
ActionConfirmed { action, success },
Error { message },
Pong,
}
Validation:
- ✅ Deserialization avec
serde_json::from_str - ⚠️ Pas de validation longueur message (MAX_MESSAGE_LENGTH non appliqué)
- ❌ Pas de sanitization XSS (ammonia présent mais non utilisé)
- ❌ Pas de validation conversation_id (existence, permissions)
3.3 Authentification WebSocket
Mécanisme: JWT dans query params ⚠️
Validation:
- ✅
JwtManager::validate_access_token(ligne 52handler.rs) - ✅ Claims extraits (
AccessTokenClaims) - ⚠️ Pas de refresh token support dans WS
- ❌ Pas de validation permissions conversation
Failles:
- Token dans URL (exposé logs, cache, referrer)
- Pas de validation conversation access avant join
- Pas de rate limiting par user_id (seulement par connection_id)
💬 SECTION 4 : FONCTIONNALITÉS CHAT
4.1 Messages 1-to-1
| Feature | Status | Implémentation | Notes |
|---|---|---|---|
| Envoi message direct | ✅ | MessageRepository::create |
UUID-based |
| Réception temps-réel | ✅ | broadcast_to_conversation |
Via WebSocket |
| Historique messages | ✅ | get_conversation_messages |
Pagination LIMIT |
| Chiffrement E2E | ❌ | Absent | Non implémenté |
Code:
// src/repository/message_repository.rs:18
pub async fn create(
&self,
conversation_id: Uuid,
sender_id: Uuid,
content: &str,
) -> Result<Message>
Gaps:
- Pas de chiffrement end-to-end
- Pagination basique (LIMIT/OFFSET, pas de cursor)
- Pas de recherche full-text
4.2 Group Chat & Channels
| Feature | Status | Implémentation | Notes |
|---|---|---|---|
| Création groupe | ✅ | RoomService::create_room |
UUID-based |
| Ajout membres | ✅ | RoomService::add_user |
Via conversation_members |
| Suppression membres | ✅ | RoomService::remove_user |
|
| Permissions | ⚠️ | Partiel | Rôles présents, vérification incomplète |
| Broadcast messages | ✅ | broadcast_to_conversation |
|
| Historique groupe | ✅ | get_conversation_messages |
Permissions:
- Rôles définis:
admin,moderator,member(migration 001) - ⚠️ Vérification permissions non appliquée dans handler WS
4.3 Features Temps-Réel
Typing Indicators
Status: ✅ Fonctionnel (75%)
Implémentation:
TypingIndicatorManager(src/typing_indicator.rs)- Timeout: 3s (hardcodé ligne 19)
- Cleanup périodique présent
- ⚠️ Pas de broadcast WebSocket intégré (manager isolé)
Code:
// src/typing_indicator.rs:25
pub async fn set_typing(&self, conversation_id: &str, user_id: &str)
Gaps:
- Pas de message WebSocket pour typing events
- Timeout non configurable
- Pas de debouncing côté client
Read Receipts
Status: 🟡 Partiel (40%)
Implémentation:
- Structure DB présente (
read_receiptstable) ReadReceiptManagerprésent (src/read_receipts.rs)- ❌ PROBLÈME P0: TODO ligne 258
websocket/handler.rs - ⚠️ Types incohérents:
i64au lieu deUUID
Code problématique:
// src/websocket/handler.rs:249
IncomingMessage::MarkAsRead { conversation_id, message_id } => {
debug!("👁️ Client {} marque le message {} comme lu", client.id, message_id);
// TODO: Implémenter la logique de marquage comme lu
let outgoing = OutgoingMessage::ActionConfirmed {
action: "marked_as_read".to_string(),
success: true,
};
client.send_message(outgoing).await?;
}
Gaps:
- Logique non implémentée
- Pas de synchronisation multi-device
- Pas de UI double-check marks
Presence
Status: ✅ Fonctionnel (70%)
Implémentation:
PresenceManager(src/presence.rs)- Statuts: Online, Away, Busy, Invisible, Offline
- Cleanup inactifs (5 min threshold)
- ⚠️ Pas de persistence DB (mémoire uniquement)
- ⚠️ Pas de broadcast WebSocket intégré
4.4 Modération
| Feature | Status | Implémentation | Notes |
|---|---|---|---|
| Détection spam | ⚠️ | Basique | Similarité Levenshtein simplifiée |
| Blocage users | ✅ | ModerationSystem::apply_sanction |
DB présente |
| Suppression messages | ✅ | MessageRepository::delete |
Soft delete (is_deleted) |
| Modération auto | ⚠️ | Basique | Seuil 3 messages similaires |
| Logs modération | ✅ | Tracing + DB | Audit trail présent |
Code modération auto:
// src/moderation.rs:270
async fn detect_spam(&self, user_id: i32, content: &str) -> Result<bool> {
// Vérifier les messages répétitifs (5 dernières minutes)
let similar_count = recent_messages.iter()
.filter(|row| {
let msg_content: String = row.get("content");
self.calculate_similarity(content, &msg_content) > 0.8
})
.count();
Ok(similar_count >= 3)
}
Problèmes:
- Détection spam trop simpliste
- Pas de ML/AI (TODO ligne 329
security.rs) - Incohérence types:
i32user_id au lieu UUID
🚀 SECTION 5 : PERFORMANCE
5.1 Benchmarks
Status: ❌ Absent
- Pas de dossier
benches/ - Criterion présent dans
Cargo.tomlmais non utilisé - Pas de métriques de performance documentées
Recommandation: Créer benchmarks pour:
- Messages/seconde supportés
- Latence p50/p95/p99
- Concurrent connections max
- Memory usage par connection
5.2 Gestion Mémoire
Allocations:
- ⚠️ Clones excessifs:
OutgoingMessage::clone()ligne 144websocket/mod.rs - ✅ Arc<RwLock<>> utilisé correctement
- ⚠️ Vec<Arc> dans WebSocketManager (croissance linéaire)
Lifetimes:
- ✅ Lifetimes Rust corrects (pas de dangling refs)
- ⚠️ Pas de vérification memory leaks (tests manquants)
Buffer sizes:
- ⚠️ Pas de limite taille message WebSocket (64KB max recommandé)
- ⚠️ Pas de backpressure handling
5.3 Concurrence
Tokio:
- ✅ Async/await partout
- ⚠️ Pas de
spawn_blocking()pour opérations CPU-intensive - ✅ Channels utilisés (tokio::sync::broadcast)
Deadlocks potentiels:
- ⚠️ Ordre locks:
WebSocketManager::clientspuisclient.conversations(ligne 141websocket/mod.rs) - ⚠️ Pas de timeout sur locks (RwLock peut bloquer indéfiniment)
Race conditions:
- ⚠️
WebSocketManager::remove_clientpeut race avecbroadcast_to_conversation
5.4 Scalabilité
Horizontal scaling:
- ❌ Pas de Redis pub/sub pour multi-instances
- ❌ Broadcast limité à instance unique
- ⚠️ RabbitMQ EventBus présent mais non utilisé pour broadcast
Session sticky:
- ❌ Pas de support load balancer aware
- ⚠️ Pas de session externalisée (Redis)
Graceful shutdown:
- ⚠️ Pas de drain connections
- ⚠️ Pas de timeout shutdown
🔒 SECTION 6 : SÉCURITÉ
6.1 Validation Input
Handlers WebSocket:
- ⚠️ Message length: Pas de vérification MAX_MESSAGE_LENGTH (2000 chars config)
- ❌ Sanitization XSS:
ammoniaprésent mais non utilisé - ⚠️ Injection: SQLx prepared statements ✅ (protection SQL)
- ❌ File upload: Non implémenté (feature flag présent)
Code problématique:
// src/websocket/handler.rs:170
IncomingMessage::SendMessage { conversation_id, content, parent_message_id } => {
// Pas de validation longueur content
// Pas de sanitization XSS
let message = state.message_repo.create(conversation_id, sender_uuid, &content).await?;
}
6.2 Rate Limiting
Implémentation:
- ✅
DosProtectionManagerprésent (src/rate_limiter.rs) - ❌ PROBLÈME P1: Non utilisé dans
websocket/handler.rs - ✅ Rate limiting par user_id et IP
- ⚠️ Configuration hardcodée (60 msg/min)
Code manquant:
// src/websocket/handler.rs devrait avoir:
let dos_manager = state.dos_manager.clone();
if !dos_manager.check_message_allowed(claims.user_id).await? {
return Err(ChatError::rate_limit_error("Rate limit exceeded"));
}
6.3 Chiffrement
TLS/WSS:
- ⚠️ Pas de configuration TLS dans
main.rs - ⚠️
ENABLE_TLS=falsedansenv.example - ❌ Pas de certificats validation
End-to-end encryption:
- ❌ Non implémenté
- ❌ Pas de Signal Protocol ou équivalent
Storage at-rest:
- ⚠️ Messages en clair dans PostgreSQL
- ⚠️ Pas de chiffrement colonnes sensibles
6.4 Audit Sécurité Rust
Clippy:
cargo clippy --all-targets --all-features -- -D warnings
Résultats:
- 10+ warnings (unused imports, PartialEq manquant)
- Pas de
unsafeblocks (bon ✅)
Cargo audit:
- ⚠️ Non exécuté (recommandé:
cargo audit)
Vulnérabilités connues:
- À vérifier avec
cargo audit
💾 SECTION 7 : PERSISTENCE
7.1 Stockage Messages
Backend: PostgreSQL via SQLx ✅
Schéma:
- Table
messages(UUID-based) - Index:
conversation_id,sender_id,created_at✅ - Soft delete:
is_deletedBOOLEAN ✅ - ⚠️ Pas de partitioning par date
- ⚠️ Pas de retention policy (purge old messages)
Migrations:
001_create_clean_database.sql✅002_advanced_features.sql✅- Schéma complet et cohérent
7.2 Queries Database
N+1 queries:
- ⚠️ Potentiel dans
get_conversation_messages(pas de JOIN users) - ✅ Pagination présente (LIMIT)
Prepared statements:
- ✅ SQLx
query!macros utilisées ✅ - ✅ Protection injection SQL
Queries lentes:
- ⚠️ Pas d'EXPLAIN ANALYZE effectué
- ⚠️ Pas de monitoring query duration
7.3 Cache Strategy
Redis:
- ⚠️ Dépendance présente mais non utilisée
- ❌ Pas de cache recent messages
- ❌ Pas de cache user online status
- ❌ Pas de cache typing indicators
- ❌ Pas de pub/sub pour broadcast multi-instances
Recommandation: Implémenter cache Redis pour:
- Messages récents (TTL 1h)
- User presence (TTL 5min)
- Typing indicators (TTL 10s)
✅ SECTION 8 : QUALITÉ CODE
8.1 Linting
Clippy violations (catégorisées):
| Catégorie | Count | Exemples |
|---|---|---|
| Correctness | 2 | PartialEq manquant SslMode |
| Performance | 0 | Aucun |
| Style | 8 | Unused imports |
| Complexity | 0 | Aucun |
Fichiers problématiques:
src/config.rs: Unused importsPool,Postgres,errorsrc/event_bus.rs: Unused importsExchangeDeclareOptions,LapinError,ExchangeKindsrc/websocket/handler.rs: Unused importSinkExt,SimpleMessageStoresrc/config.rs:552:SslModemanque#[derive(PartialEq)]
8.2 Formatting
Status: ⚠️ Non vérifié
Recommandation: Exécuter cargo fmt --check
8.3 Tests
Coverage estimé: 25%
Tests présents:
tests/integration_test.rs: 6 tests, tous#[ignore]- Tests unitaires: Rares (typing_indicator, jwt_manager, rate_limiter)
Modules sans tests:
websocket/handler.rs❌repository/message_repository.rs❌services/room_service.rs❌moderation.rs❌presence.rs❌
Objectif ORIGIN_: 75%+ coverage (non atteint)
8.4 Documentation
Doc comments:
- ✅ Présents sur modules principaux (
//!) - ⚠️ Manquants sur fonctions publiques
- ❌ Pas d'exemples dans doc (
#[doc = include_str!("...")])
Cargo doc:
- ⚠️ Non généré (recommandé:
cargo doc --no-deps --open)
📈 SECTION 9 : OBSERVABILITÉ
9.1 Logging
Framework: tracing ✅
Structured logging:
- ✅ Utilisé partout
- ✅ Niveaux: trace/debug/info/warn/error
- ⚠️ Pas de logs sensibles détectés (tokens, passwords)
Configuration:
- JSON en production (ligne 106
main.rs) - Debug en dev (ligne 109
main.rs)
9.2 Metrics
Prometheus:
- ✅
PrometheusMetricsdéfini (src/prometheus_metrics.rs) - ❌ PROBLÈME P2: Métriques non enregistrées dans handlers
- ✅ Endpoint
/metricsprésent (ligne 203main.rs)
Métriques manquantes:
- Messages/seconde non enregistrés
- Latence messages non mesurée
- Erreurs non comptées
9.3 Tracing
OpenTelemetry:
- ❌ Non implémenté
- ❌ Pas de spans (connection lifecycle, message flow)
- ❌ Pas de trace_id propagation
🔗 SECTION 10 : INTÉGRATION
10.1 API REST
Endpoints exposés:
GET /health✅GET /healthz✅ (liveness)GET /readyz✅ (readiness)GET /metrics✅ (Prometheus)GET /api/messages/{conversation_id}✅POST /api/messages✅GET /api/messages/stats⚠️ (hardcodé)
Cohérence avec backend Go:
- ⚠️ Non vérifiée (backend Go non analysé)
10.2 Communication Inter-Services
RabbitMQ EventBus:
- ✅
RabbitMQEventBusprésent (src/event_bus.rs) - ⚠️ Initialisé dans
main.rsmais non utilisé pour events - ✅ Retry logic présent
- ⚠️ Pas de circuit breaker
gRPC:
- ⚠️
grpc_server.rsprésent mais non démarré - ⚠️
grpc_client.rsprésent mais non utilisé
10.3 Authentification Partagée
JWT shared secret:
- ✅
JWT_SECRETenv var - ⚠️ Fallback hardcodé (ligne 162
main.rs) - ✅ Même algorithme (HS256 par défaut)
- ⚠️ Audience/Issuer configurables
Incohérences:
- ⚠️ Non vérifiées (backend Go non analysé)
📐 SECTION 11 : GAP ANALYSIS ORIGIN_
11.1 Matrice Complétude Features
| Feature ORIGIN_ | Status | Complétude | Gaps |
|---|---|---|---|
| WebSocket temps-réel | ✅ | 80% | Heartbeat timeout, idle connections |
| Messages 1-to-1 | ✅ | 80% | E2E encryption manquant |
| Group chat | ✅ | 70% | Permissions incomplètes |
| Channels | ⚠️ | 50% | Structure présente, features manquantes |
| Typing indicators | ✅ | 75% | Broadcast WS manquant |
| Read receipts | 🟡 | 40% | TODO ligne 258 |
| Presence | ✅ | 70% | Persistence DB manquante |
| Modération auto | ⚠️ | 60% | ML/AI manquant |
| Rate limiting | ⚠️ | 50% | Non appliqué dans handler |
| Redis pub/sub | ❌ | 0% | Non implémenté |
| Metrics Prometheus | ⚠️ | 30% | Définies mais non utilisées |
| Tests coverage 75%+ | ❌ | 25% | Tests ignorés, unitaires rares |
11.2 Écarts Architecture
ORIGIN_ attendu:
- Event-driven via RabbitMQ ✅ (présent mais non utilisé)
- Redis pub/sub pour scaling ❌ (non implémenté)
- gRPC inter-services ⚠️ (présent mais non démarré)
Architecture réelle:
- WebSocket direct (Axum)
- Broadcast in-memory (tokio::sync::broadcast)
- Pas de scaling horizontal
🎯 SECTION 12 : PLAN D'ACTION PRIORISÉ
RUST-CHAT-001 | Auth WebSocket Token dans Query Params
├─ Gravité : 🔴 P0 - BLOQUANT
├─ Description : Token JWT exposé dans URL query params (ligne 43 handler.rs)
├─ Impact : Token visible dans logs, cache navigateur, referrer headers
├─ Effort : 2-3 heures
├─ Dépendances : Aucune
└─ Action : Déplacer token dans header Authorization ou cookie HTTP-only
Solution:
// Avant (handler.rs:43)
let token = params.get("token")?;
// Après
let auth_header = request.headers().get("authorization")?;
let token = extract_token_from_header(auth_header.to_str()?)?;
RUST-CHAT-002 | Read Receipts Non Implémentés
├─ Gravité : 🔴 P0 - BLOQUANT
├─ Description : TODO ligne 258 websocket/handler.rs - logique marquage comme lu absente
├─ Impact : Feature read receipts non fonctionnelle
├─ Effort : 1 jour
├─ Dépendances : ReadReceiptManager présent mais types i64 vs UUID
└─ Action : Implémenter logique + corriger types UUID
Solution:
// handler.rs:249
IncomingMessage::MarkAsRead { conversation_id, message_id } => {
let read_receipt_manager = state.read_receipt_manager.clone();
read_receipt_manager.mark_as_read(
message_id,
claims.user_id.parse()?,
conversation_id
).await?;
// Broadcast read receipt to conversation
state.ws_manager.broadcast_read_receipt(conversation_id, message_id, claims.user_id).await?;
}
RUST-CHAT-003 | Tests Coverage < 30%
├─ Gravité : 🔴 P0 - BLOQUANT
├─ Description : Tests d'intégration ignorés, unitaires rares
├─ Impact : Pas de confiance dans refactoring, bugs non détectés
├─ Effort : 5-7 jours
├─ Dépendances : Setup DB de test
└─ Action : Activer tests integration, ajouter tests unitaires critiques
Actions:
- Retirer
#[ignore]des tests integration - Ajouter tests unitaires:
websocket/handler.rs,message_repository.rs - Setup CI/CD avec coverage reporting (tarpaulin)
RUST-CHAT-004 | Clippy Warnings Non Résolus
├─ Gravité : 🟠 P1 - CRITIQUE
├─ Description : 10+ warnings compilation (unused imports, PartialEq manquant)
├─ Impact : Code quality dégradée, warnings masquent vrais problèmes
├─ Effort : 1-2 heures
├─ Dépendances : Aucune
└─ Action : Corriger tous les warnings clippy
Actions:
- Supprimer unused imports
- Ajouter
#[derive(PartialEq)]àSslMode - Ajouter
#[allow(dead_code)]si nécessaire (temporaire)
RUST-CHAT-005 | Rate Limiting Non Appliqué
├─ Gravité : 🟠 P1 - CRITIQUE
├─ Description : DosProtectionManager présent mais non utilisé dans handler
├─ Impact : Pas de protection DoS, spam possible
├─ Effort : 2-3 heures
├─ Dépendances : DosProtectionManager présent
└─ Action : Intégrer rate limiting dans websocket handler
Solution:
// Ajouter dans WebSocketState
pub dos_manager: Arc<DosProtectionManager>,
// Dans handle_incoming_message
if !state.dos_manager.check_message_allowed(claims.user_id.parse()?).await? {
return Err(ChatError::rate_limit_error("Rate limit exceeded"));
}
RUST-CHAT-006 | Validation Input Messages
├─ Gravité : 🟠 P1 - CRITIQUE
├─ Description : Pas de sanitization XSS, longueur max non vérifiée
├─ Impact : XSS possible, DoS via messages géants
├─ Effort : 1 jour
├─ Dépendances : ammonia présent, validator présent
└─ Action : Ajouter validation longueur + sanitization XSS
Solution:
// handler.rs:170
const MAX_MESSAGE_LENGTH: usize = 2000;
if content.len() > MAX_MESSAGE_LENGTH {
return Err(ChatError::validation_error("Message too long"));
}
let sanitized = ammonia::clean(content);
let message = state.message_repo.create(conversation_id, sender_uuid, &sanitized).await?;
RUST-CHAT-007 | Heartbeat/Ping-Pong Incomplet
├─ Gravité : 🟠 P1 - CRITIQUE
├─ Description : Ping géré, mais pas de timeout idle connections
├─ Impact : Connections zombies consomment ressources
├─ Effort : 1 jour
├─ Dépendances : Aucune
└─ Action : Implémenter timeout idle + heartbeat périodique
Solution:
// Ajouter dans handle_socket
let mut last_ping = Instant::now();
const IDLE_TIMEOUT: Duration = Duration::from_secs(300); // 5 min
tokio::select! {
msg = receiver.next() => { /* ... */ },
_ = tokio::time::sleep(Duration::from_secs(30)) => {
// Heartbeat périodique
if last_ping.elapsed() > IDLE_TIMEOUT {
break; // Timeout
}
client.send_message(OutgoingMessage::Ping).await?;
}
}
RUST-CHAT-008 | Modération Auto Basique
├─ Gravité : 🟡 P2 - MAJEUR
├─ Description : Détection spam simpliste (similarité Levenshtein)
├─ Impact : Faux positifs/négatifs, spam non détecté
├─ Effort : 3-5 jours
├─ Dépendances : ML model (optionnel)
└─ Action : Améliorer détection spam (rate limiting + patterns)
Solution:
- Combiner rate limiting + similarité
- Ajouter détection patterns (URLs répétées, mentions massives)
- Intégrer ML model (optionnel, TODO ligne 329)
RUST-CHAT-009 | Pas de Redis Pub/Sub
├─ Gravité : 🟡 P2 - MAJEUR
├─ Description : Broadcast limité à instance unique
├─ Impact : Pas de scaling horizontal
├─ Effort : 3-5 jours
├─ Dépendances : Redis présent mais non utilisé
└─ Action : Implémenter Redis pub/sub pour broadcast multi-instances
Solution:
// Ajouter Redis pub/sub dans broadcast.rs
pub async fn broadcast_to_conversation_redis(
&self,
conversation_id: Uuid,
message: OutgoingMessage,
) -> Result<()> {
// Publier sur Redis channel
let channel = format!("conversation:{}", conversation_id);
let json = serde_json::to_string(&message)?;
redis_client.publish(channel, json).await?;
Ok(())
}
RUST-CHAT-010 | Metrics Prometheus Non Utilisées
├─ Gravité : 🟡 P2 - MAJEUR
├─ Description : Métriques définies mais non enregistrées dans handlers
├─ Impact : Pas de monitoring production
├─ Effort : 1 jour
├─ Dépendances : PrometheusMetrics présent
└─ Action : Enregistrer métriques dans tous les handlers
Solution:
// Ajouter dans WebSocketState
pub metrics: Arc<PrometheusMetrics>,
// Dans handle_incoming_message
state.metrics.record_message_received(content.len() as u64);
let start = Instant::now();
// ... traitement ...
state.metrics.record_message_processing_duration(start.elapsed());
RUST-CHAT-011 | Incohérence Types i32 vs UUID
├─ Gravité : 🟡 P2 - MAJEUR
├─ Description : read_receipts.rs et moderation.rs utilisent i32 au lieu UUID
├─ Impact : Incompatibilité avec schéma DB (UUID)
├─ Effort : 1 jour
├─ Dépendances : Migration DB vers UUID
└─ Action : Migrer read_receipts et moderation vers UUID
RUST-CHAT-012 | Modules Non Utilisés (Code Mort)
├─ Gravité : 🟢 P3 - MINEUR
├─ Description : core/ et hub/ présents mais non utilisés
├─ Impact : Code mort, confusion
├─ Effort : 2 heures
├─ Dépendances : Aucune
└─ Action : Supprimer ou intégrer modules
RUST-CHAT-013 | Duplication Auth Modules
├─ Gravité : 🟢 P3 - MINEUR
├─ Description : auth.rs, authentication.rs, jwt_manager.rs (overlap)
├─ Impact : Confusion, maintenance difficile
├─ Effort : 1 jour
├─ Dépendances : Aucune
└─ Action : Consolider en un seul module auth
RUST-CHAT-014 | Pas de Graceful Shutdown
├─ Gravité : 🟡 P2 - MAJEUR
├─ Description : Pas de drain connections au shutdown
├─ Impact : Perte messages en cours, connections brutalement fermées
├─ Effort : 1 jour
├─ Dépendances : Tokio signal
└─ Action : Implémenter graceful shutdown avec drain
Solution:
// main.rs
tokio::select! {
result = axum::serve(listener, app) => { result? },
_ = tokio::signal::ctrl_c() => {
info!("🛑 Shutdown signal reçu, drain connections...");
// Drain WebSocket connections
ws_manager.drain_all().await;
info!("✅ Shutdown gracieux terminé");
}
}
📝 CONCLUSION
Le Chat Server Rust présente une base solide avec:
- ✅ Architecture WebSocket moderne (Axum)
- ✅ Persistence PostgreSQL robuste (SQLx)
- ✅ Structure modulaire claire
- ✅ Features temps-réel partiellement implémentées
Problèmes critiques à résoudre en priorité:
- 🔴 Auth WebSocket (token dans URL)
- 🔴 Read receipts non implémentés
- 🔴 Tests coverage insuffisant
- 🟠 Rate limiting non appliqué
- 🟠 Validation input manquante
Effort total estimé: 18-27 jours pour atteindre production-ready avec toutes les features ORIGIN_.
Recommandation: Commencer par P0 (3-5 jours), puis P1 (5-7 jours) avant déploiement production.
Fin du rapport d'audit