# Audit Technique Exhaustif — Module Frontend Veza (`apps/web`) **Date**: 2025-12-12 **Auditeur**: Auto (Cursor AI) **Module**: Frontend React (Vite + TypeScript) **Version**: 1.0.0 --- ## Table des matières 1. [Module Overview + Runbook](#module-overview--runbook) 2. [Health/Build/Test Status](#healthbuildtest-status) 3. [Security Findings](#security-findings) 4. [Observability & Prod Readiness Gaps](#observability--prod-readiness-gaps) 5. [Performance Notes](#performance-notes) 6. [Issue List Priorisée (P0→P3)](#issue-list-priorisée-p0p3) 7. [Execution Plan](#execution-plan) --- # Module Overview + Runbook ## But du module Le module `apps/web` est l'application frontend React de la plateforme Veza, une plateforme audio collaborative avec streaming, chat en temps réel, bibliothèque musicale, playlists collaboratives, et marketplace. **Rôle exact** : Interface utilisateur web (SPA) pour : - Authentification (JWT, 2FA, OAuth) - Streaming audio (HLS.js, WebRTC) - Chat en temps réel (WebSocket) - Gestion de bibliothèque et playlists - Marketplace de tracks - Profils utilisateurs et collaboration ## Entrées / Sorties ### APIs exposées (consommées) **HTTP REST** : - Base URL : `VITE_API_BASE_URL` (défaut: `http://localhost:8080/api/v1`) - Client : Axios (`src/services/api.ts`, `src/services/api/client.ts`) - Endpoints principaux : - `/auth/*` : login, register, refresh, logout, me - `/users/*` : CRUD utilisateurs - `/tracks/*` : CRUD tracks, upload, download - `/library/*` : bibliothèque utilisateur - `/conversations/*`, `/messages/*` : chat - `/playlists/*` : playlists **WebSocket** : - URL : `VITE_WS_BASE_URL` (défaut: `ws://localhost:8081/ws`) - Service : `src/services/websocket.ts` - Protocole : JSON messages avec types (`join_conversation`, `send_message`, `typing`, etc.) **Streaming** : - URL : `VITE_STREAM_URL` (défaut: `http://localhost:8082`) - Protocole : HLS.js pour audio streaming ### Formats - **JSON** : Toutes les APIs REST et WebSocket - **FormData** : Upload de fichiers (tracks, avatars) - **Schémas Zod** : Validation côté client (`src/services/api.ts`) ## Dépendances internes - **Stores Zustand** : `src/features/auth/store/authStore.ts` (moved from stores/auth.ts), `src/features/chat/store/chatStore.ts` (moved from stores/chat.ts), `src/stores/library.ts`, `src/stores/ui.ts`, `src/stores/cartStore.ts` - **Types partagés** : `src/types/api.ts`, `src/types/index.ts` - **Utils** : `src/utils/*` (sanitize, token-manager, validation, etc.) - **Hooks** : `src/hooks/*` (useAuth, useDebounce, usePWA, etc.) ## Dépendances externes - **Base de données** : Aucune (consomme API backend Go) - **Redis** : Aucune (consomme via backend) - **Storage** : `localStorage` (tokens), `sessionStorage` (refresh tokens), IndexedDB (PWA cache) - **Services externes** : Pas de dépendances directes (tout via backend) ## Exécution ### Commandes de build/run/dev ```bash # Dev npm run dev # Vite dev server (port 3000) npm run dev:lab # Dev avec vraie BDD (via scripts/start_lab.sh) npm run dev:mocks # Dev avec MSW mocks # Build npm run build # Production build → dist/ # Tests npm test # Vitest (unit) npm run test:e2e # Playwright E2E npm run test:e2e:mocks # E2E avec MSW # Qualité npm run typecheck # TypeScript check npm run lint # ESLint npm run format # Prettier ``` ### Configuration **Variables d'environnement** (validées via Zod dans `src/config/env.ts`) : - `VITE_API_BASE_URL` : URL API REST - `VITE_WS_BASE_URL` : URL WebSocket - `VITE_STREAM_URL` : URL streaming - `VITE_UPLOAD_URL` : URL upload - `VITE_APP_NAME` : Nom de l'app - `VITE_DEBUG` : Mode debug (boolean) - `VITE_USE_MSW` : Activer MSW mocks - `VITE_FCM_VAPID_KEY` : Push notifications (optionnel) **Fichiers de config** : - `vite.config.ts` : Configuration Vite (aliases, plugins, CSP) - `tsconfig.json` : TypeScript config - `vitest.config.ts` : Tests config - `eslint.config.js` : ESLint config - `nginx.conf` : Config production (Docker) - `Dockerfile` : Build production ### Docker ```bash docker build -t veza-frontend . docker run -p 80:80 veza-frontend ``` ## Points d'intégration ### Contrats d'API **Backend Go** (`veza-backend-api`) : - Format réponse : `{ success: true, data: {...} }` ou `{ error: "...", message: "..." }` - Auth : JWT dans header `Authorization: Bearer ` - IDs : Backend retourne `int64`, frontend attend `string` (conversion nécessaire) **Chat Server Rust** (`veza-chat-server`) : - WebSocket sur `/ws` - Token JWT en query param : `?token=` - Messages JSON : `{ type: string, data: any }` **Stream Server Rust** (`veza-stream-server`) : - HLS streaming via HTTP - WebSocket pour synchronisation playback ### Auth - **JWT** : Access token (15min) + Refresh token (30/90 jours selon `remember_me`) - **Storage** : `localStorage` (access), `sessionStorage`/cookie (refresh) - **Refresh automatique** : Intercepteur Axios (`src/services/api/client.ts`) - **2FA** : Supporté via `src/services/2fa-service.ts` - **OAuth** : Supporté via `src/features/auth/hooks/useOAuthCallback.ts` ### Headers / CORS - CORS géré par backend - Headers de sécurité : CSP, X-Frame-Options, etc. (via `vite.config.ts` et `nginx.conf`) ### Schéma DB / UUID - **IDs** : Backend retourne `int64`, frontend utilise `string` (conversion dans `src/services/api.ts`) - **UUID** : Pas utilisé (IDs numériques) --- # Health/Build/Test Status ## Build Status ### TypeScript Errors **Total** : ~100+ erreurs TypeScript (voir `ts_real_errors.log`) **Catégories principales** : 1. **Modules manquants** (P0) : - `@/hooks/use-toast` : utilisé mais non exporté/existant - `@/stores/player` : utilisé mais non existant - `@/components/ui/progress` : utilisé mais non existant - `@/hooks/useIntersectionObserver` : utilisé mais non existant - `@/features/settings/pages/SettingsPage` : importé mais chemin incorrect - `@/features/profile/pages/UserProfilePage` : importé mais chemin incorrect - `@/features/roles/pages/RolesPage` : importé mais chemin incorrect - `@/features/tracks/pages/TrackDetailPage` : importé mais chemin incorrect - `@/features/playlists/routes` : importé mais chemin incorrect - `@/pages/marketplace/MarketplaceHome` : importé mais chemin incorrect 2. **Types incorrects** (P1) : - `Track` : propriétés manquantes (`url`, `cover_url`, `album`, `file_size`, `is_playing`, `progress`) - `User` : propriétés manquantes (`userId`, `username`) - `PaginatedResponse` : propriété `tracks` manquante - `ChatPage` : export par défaut manquant (lazy loading) 3. **React Query v5 incompatibilité** (P1) : - `onSuccess` déprécié (remplacé par `useEffect` + `data`) - `keepPreviousData` déprécié (remplacé par `placeholderData: keepPreviousData`) 4. **Types implicites `any`** (P2) : ~20 occurrences ### ESLint Warnings **Total** : ~50+ warnings **Principaux** : - `no-console` : 121 occurrences (devrait être supprimé en prod) - `@typescript-eslint/no-explicit-any` : ~30 occurrences - `@typescript-eslint/no-unused-vars` : 2 erreurs (variables non utilisées) - `react-hooks/exhaustive-deps` : 1 warning ## Test Status ### Coverage **Configuré** : 80% threshold (branches, functions, lines, statements) **État actuel** : Non mesuré (pas de rapport de coverage visible) ### Tests unitaires (Vitest) **Total** : 189 fichiers de test (`.test.ts`, `.test.tsx`) **Résultats** : - ✅ La plupart des tests passent - ❌ **2 tests échouent** : 1. `src/features/playlists/hooks/usePlaylistTrack.test.tsx` : `useAddTrackToPlaylist` — argument manquant 2. `src/features/playlists/hooks/usePlaylistTrack.test.tsx` : `useRemoveTrackFromPlaylist` — fonction non exportée **Problèmes identifiés** : - MSW handlers manquants pour certains endpoints WebSocket - Mocks incomplets pour certains services ### Tests E2E (Playwright) **Config** : `playwright.config.ts` **Tests** : `e2e/qa-audit.spec.ts` **État** : Non exécuté dans cet audit ## Gestion des erreurs ### Points positifs - ErrorBoundary React (`src/components/ErrorBoundary.tsx`) - Intercepteurs Axios pour refresh token automatique - Validation Zod pour les réponses API ### Points à améliorer - Pas de centralisation des erreurs (chaque composant gère ses erreurs) - Pas de logging structuré (console.log partout) - Pas de retry automatique pour les requêtes échouées (sauf refresh token) ## Cohérence des conventions ### Naming - ✅ Cohérent : PascalCase pour composants, camelCase pour fonctions - ⚠️ Incohérences : - `useAuthStore` vs `useAuth` (deux hooks différents) - `apiService` vs `apiClient` (deux clients API différents) - `websocketService` vs `wsService` (aliases, confusion) ### Structure dossiers - ✅ Feature-based : `src/features/*` - ✅ Séparation claire : components, hooks, services, stores - ⚠️ Duplication : `src/services/api.ts` et `src/services/api/client.ts` (deux implémentations) ### Séparation couches - ✅ Services séparés des composants - ✅ Stores Zustand pour state management - ⚠️ Logique métier parfois dans les composants (ex: `src/features/library/components/LibraryManager.tsx`) --- # Security Findings ## Top 10 Risques Sécurité ### P0 — Critiques (Exploitables immédiatement) #### 1. **Tokens JWT stockés en localStorage (XSS vulnérable)** **Fichiers** : - `src/services/api.ts` : lignes 105-121 - `src/services/api/auth.ts` : lignes 64-68, 124-126 - `src/utils/token-manager.ts` : lignes 15-34, 40-54 **Problème** : ```typescript // src/services/api.ts:105-106 private getAccessToken(): string | null { return localStorage.getItem('access_token'); } ``` Les tokens JWT sont stockés dans `localStorage`, vulnérables aux attaques XSS. Un script malveillant injecté peut voler les tokens. **Impact** : Vol de session, accès non autorisé aux comptes utilisateurs **Fix minimal** : - Option 1 : Utiliser `httpOnly` cookies (nécessite backend) - Option 2 : Utiliser `sessionStorage` + rotation fréquente des tokens - Option 3 : Implémenter un service worker pour isoler les tokens **Plan de validation** : - Test XSS : Injecter `` - Vérifier que les tokens ne sont pas accessibles via XSS **Effet de bord** : Nécessite modification backend pour cookies httpOnly **Effort** : M (2-4h) --- #### 2. **CSP non-strict en production (`unsafe-inline`, `unsafe-eval`)** **Fichiers** : - `vite.config.ts` : lignes 62-75 **Problème** : ```typescript 'script-src': [ "'self'", "'unsafe-inline' 'unsafe-eval' blob:", // ⚠️ Dangereux ], 'style-src': [ "'self'", "'unsafe-inline'", // ⚠️ Nécessaire pour Tailwind mais risqué ], ``` La CSP en dev contient `unsafe-inline` et `unsafe-eval`, permettant l'exécution de scripts inline (XSS). **Impact** : Attaques XSS facilitées **Fix minimal** : - Utiliser des nonces CSP pour les scripts inline (déjà partiellement implémenté dans `src/utils/csp.ts`) - Supprimer `unsafe-eval` en production - Utiliser `nonce-` pour les styles inline si possible **Plan de validation** : - Tester avec CSP strict en production - Vérifier que tous les scripts ont des nonces **Effet de bord** : Peut casser certains composants si nonces mal gérés **Effort** : S (1-2h) --- #### 3. **Utilisation de `dangerouslySetInnerHTML` sans sanitisation complète** **Fichiers** : - `src/features/chat/components/ChatMessages.tsx` : ligne 102 - `src/features/chat/components/VirtualizedChatMessages.tsx` : ligne 75 **Problème** : ```typescript // src/features/chat/components/ChatMessages.tsx:102 dangerouslySetInnerHTML={{ __html: sanitizeChatMessage(message.content) }} ``` Bien que `sanitizeChatMessage` soit utilisé, la fonction `sanitizeHTML` dans `src/utils/sanitize.ts` peut avoir des failles (regex-based, pas de parser HTML complet). **Impact** : XSS via messages de chat **Fix minimal** : - Utiliser `DOMPurify` (déjà dans `package.json` : `dompurify@^3.3.0`) au lieu de la sanitisation custom - Vérifier que `DOMPurify` est bien utilisé partout **Plan de validation** : - Test XSS : Envoyer `` dans un message - Vérifier que le script ne s'exécute pas **Effet de bord** : Aucun (DOMPurify est déjà une dépendance) **Effort** : S (1h) --- #### 4. **CSRF Token non implémenté** **Fichiers** : - `src/services/csrf.ts` : lignes 1-34 **Problème** : ```typescript // src/services/csrf.ts:14-17 public async refreshCsrfToken(): Promise { // Placeholder: fetch from backend if needed // this.csrfToken = ... } ``` Le service CSRF existe mais n'est pas implémenté. Aucune protection CSRF pour les requêtes POST/PUT/DELETE. **Impact** : Attaques CSRF possibles (actions non autorisées) **Fix minimal** : - Implémenter `refreshCsrfToken()` pour récupérer le token depuis le backend - Ajouter le header `X-CSRF-Token` dans tous les intercepteurs Axios - Backend doit générer et valider les tokens CSRF **Plan de validation** : - Test CSRF : Créer une requête POST depuis un site externe - Vérifier que la requête est rejetée sans token CSRF valide **Effet de bord** : Nécessite modification backend **Effort** : M (2-3h) --- #### 5. **WebSocket token en query param (exposé dans logs/URLs)** **Fichiers** : - `src/services/api.ts` : ligne 380 - `src/services/websocket.ts` : ligne 45 **Problème** : ```typescript // src/services/api.ts:378-381 getWebSocketUrl(): string { const token = this.getAccessToken(); return `${WS_BASE_URL}?token=${token}`; } ``` Le token JWT est passé en query param, exposé dans : - Logs serveur - Historique navigateur - URLs partagées **Impact** : Fuite de token via logs/historique **Fix minimal** : - Utiliser le header `Authorization` si le serveur WebSocket le supporte - Sinon, utiliser un token temporaire (one-time token) généré par le backend **Plan de validation** : - Vérifier que le token n'apparaît pas dans les logs serveur - Vérifier que l'historique navigateur ne contient pas le token **Effet de bord** : Nécessite modification serveur WebSocket Rust **Effort** : M (2-3h) --- ### P1 — Haute priorité (Bugs fréquents / Dette bloquante) #### 6. **Validation JWT côté client absente (expiration non vérifiée)** **Fichiers** : - `src/services/api/client.ts` : lignes 11-78 - `src/utils/token-manager.ts` : lignes 40-54 **Problème** : Aucune vérification de l'expiration du token JWT côté client. Le token peut être expiré mais toujours utilisé jusqu'à ce que le backend renvoie 401. **Impact** : Requêtes inutiles avec token expiré, UX dégradée **Fix minimal** : - Décoder le JWT (base64) et vérifier `exp` avant chaque requête - Refresh automatique si expiration < 5min **Plan de validation** : - Test : Créer un token expiré, vérifier qu'il est refresh automatiquement **Effet de bord** : Aucun **Effort** : S (1h) --- #### 7. **Pas de rate limiting côté client** **Fichiers** : - `src/services/api.ts` : toutes les méthodes - `src/services/websocket.ts` : méthode `send()` **Problème** : Aucune protection contre le spam de requêtes (rate limiting). Un utilisateur malveillant peut spammer l'API. **Impact** : DoS côté client, surcharge serveur **Fix minimal** : - Implémenter un rate limiter simple (ex: max 10 requêtes/seconde) - Utiliser un debounce pour les requêtes répétées **Plan de validation** : - Test : Envoyer 100 requêtes simultanées, vérifier que seules 10 passent **Effet de bord** : Peut ralentir certaines opérations légitimes **Effort** : S (1-2h) --- #### 8. **Secrets potentiels dans le code (VITE_FCM_VAPID_KEY)** **Fichiers** : - `src/config/env.ts` : ligne 18 - `vite.config.ts` : pas de validation **Problème** : `VITE_FCM_VAPID_KEY` est une clé publique (VAPID), mais elle est chargée côté client. Si elle est mal configurée, elle pourrait exposer une clé privée. **Impact** : Fuite de clé si erreur de configuration **Fix minimal** : - Documenter que `VITE_FCM_VAPID_KEY` doit être la clé **publique** uniquement - Ajouter une validation pour s'assurer que ce n'est pas une clé privée **Plan de validation** : - Vérifier que la clé dans `.env` est bien publique (commence par `BK` ou similaire) **Effet de bord** : Aucun **Effort** : S (30min) --- #### 9. **Pas de validation de taille de fichier upload côté client** **Fichiers** : - `src/features/library/components/UploadModal.tsx` - `src/features/tracks/components/TrackUpload.tsx` **Problème** : Aucune validation de la taille de fichier avant upload. Un utilisateur peut tenter d'uploader un fichier de 10GB. **Impact** : Surcharge serveur, DoS **Fix minimal** : - Valider la taille max (ex: 100MB) avant upload - Afficher une erreur claire si dépassement **Plan de validation** : - Test : Tenter d'uploader un fichier > 100MB, vérifier que c'est rejeté **Effet de bord** : Aucun **Effort** : S (1h) --- #### 10. **CSP middleware non utilisé (créé mais jamais appelé)** **Fichiers** : - `src/utils/csp.ts` : lignes 185-202 **Problème** : ```typescript // src/utils/csp.ts:185-202 export function createCSPMiddleware() { return (_req: any, res: any, next: any) => { // ... génère CSP header } } ``` Le middleware CSP est créé mais jamais utilisé (c'est du code Node.js dans un frontend React). La CSP est gérée via `vite.config.ts` uniquement. **Impact** : CSP peut être inconsistante entre dev et prod **Fix minimal** : - Supprimer le middleware inutile (code mort) - S'assurer que la CSP dans `vite.config.ts` est correcte pour prod **Plan de validation** : - Vérifier que la CSP en prod est stricte (pas d'`unsafe-inline`) **Effet de bord** : Aucun (code mort) **Effort** : S (30min) --- ## Autres problèmes sécurité (P2/P3) ### P2 — Moyenne priorité - **Pas de validation de format de fichier upload** : Seule la validation backend existe - **Pas de protection contre clickjacking** : `X-Frame-Options: SAMEORIGIN` au lieu de `DENY` dans `nginx.conf` - **Pas de HSTS** : Header `Strict-Transport-Security` manquant dans `nginx.conf` - **Logs console en production** : 121 occurrences de `console.log/error/warn` (peuvent exposer des infos sensibles) ### P3 — Basse priorité - **Pas de validation de rate limiting pour WebSocket** : Spam de messages possible - **Pas de chiffrement des données sensibles en localStorage** : Tokens non chiffrés (mais localStorage est déjà vulnérable) --- # Observability & Prod Readiness Gaps ## Logs structurés ### État actuel ❌ **Aucun logging structuré** : - Utilisation de `console.log/error/warn` partout (121 occurrences) - Pas de corrélation (pas de `request_id` / `trace_id`) - Pas de niveaux de log cohérents - Logs en production (devrait être désactivé) **Fichiers concernés** : - `src/services/websocket.ts` : 6 occurrences - `src/features/streaming/services/playbackAnalyticsService.ts` : 11 occurrences - `src/app/App.tsx` : 1 occurrence - Et 50+ autres fichiers ### Fix recommandé - Implémenter un logger structuré (ex: `pino` ou `winston` côté client) - Ajouter `request_id` pour chaque requête API - Désactiver les logs en production (`if (import.meta.env.DEV)`) **Effort** : M (2-3h) --- ## Metrics ### État actuel ❌ **Aucune métrique** : - Pas de Prometheus / StatsD - Pas de métriques custom (erreurs, latence, etc.) - Pas de healthcheck détaillé ### Healthcheck ✅ **Basique** : `/health` endpoint dans `nginx.conf` (ligne 26-30) - Retourne simplement `200 "healthy\n"` - Pas de vérification de dépendances (API, WebSocket, etc.) ### Fix recommandé - Ajouter un healthcheck frontend qui vérifie : - Connectivité API - Connectivité WebSocket - État du storage (localStorage disponible) - Exposer des métriques custom (optionnel, via API backend) **Effort** : S (1-2h) --- ## Timeouts / Retries ### État actuel ⚠️ **Partiel** : - Timeout Axios : 10s (`src/services/api.ts:58`) - Retry : Seulement pour refresh token (intercepteur) - Pas de retry pour autres erreurs réseau - Pas de circuit breaker ### Fix recommandé - Implémenter un retry avec backoff exponentiel pour les erreurs réseau (5xx, timeout) - Ajouter un circuit breaker pour éviter de spammer un serveur down - Configurer des timeouts différents selon le type de requête (upload: 60s, API: 10s) **Effort** : M (2-3h) --- ## Gestion de charge ### État actuel ⚠️ **Basique** : - Pas de pool de connexions (Axios gère automatiquement) - Pas de limite de connexions WebSocket simultanées - Pas de chunking pour les uploads volumineux ### Uploads ✅ **Chunked upload** : Implémenté dans `src/features/tracks/services/chunkedUploadService.ts` - Upload par chunks pour fichiers volumineux - Retry automatique en cas d'échec ### WebSocket ⚠️ **Pas de backpressure** : - Pas de limite de messages en queue - Pas de gestion de reconnexion intelligente (backoff exponentiel partiel) **Fix recommandé** : - Limiter la queue de messages WebSocket (max 100 messages) - Implémenter un backpressure (pause si queue > 50) **Effort** : S (1-2h) --- ## Migrations / Compatibilité ### État actuel ⚠️ **Conversion IDs** : - Backend retourne `int64`, frontend attend `string` - Conversion manuelle dans `src/services/api.ts` (lignes 339-344, 361-374) **Problème** : Risque d'erreur si conversion oubliée **Fix recommandé** : - Créer un utilitaire centralisé pour la conversion - Utiliser des types TypeScript stricts pour forcer la conversion **Effort** : S (1h) --- # Performance Notes ## Hotspots évidents ### 1. **Bundle size** **État** : Non mesuré (bundle analyzer présent mais non exécuté) **Fichiers** : - `vite.config.ts` : lignes 34-39 (bundle analyzer configuré) - `dist/bundle-analysis.html` : présent mais non analysé **Recommandations** : - Analyser le bundle (`npm run build` puis ouvrir `dist/bundle-analysis.html`) - Identifier les dépendances lourdes - Code splitting par route (déjà fait via lazy loading) **Effort** : S (30min pour analyse) --- ### 2. **Re-renders inutiles** **Problèmes identifiés** : - `src/components/data/Table.tsx` : ligne 102 — `useCallback` avec dépendance manquante - Pas de `React.memo` sur les composants lourds (liste de tracks, messages chat) **Fix recommandé** : - Ajouter `React.memo` sur les composants de liste - Corriger les dépendances de `useCallback`/`useMemo` **Effort** : M (2-3h) --- ### 3. **JSON parsing répétitif** **Fichiers** : - `src/services/websocket.ts` : ligne 59 — `JSON.parse` à chaque message **Impact** : Faible (WebSocket messages sont petits) **Fix recommandé** : Aucun (optimisation prématurée) --- ### 4. **N+1 queries potentielles** **Fichiers** : - `src/features/library/components/LibraryManager.tsx` : chargement de tracks un par un possible **Impact** : Moyen (si beaucoup de tracks) **Fix recommandé** : - Utiliser la pagination côté API - Charger les tracks par batch **Effort** : S (1h) --- ## Streaming ### HLS.js ✅ **Bien configuré** : Utilisation de `hls.js@^1.6.14` - Buffering automatique - Adaptation de bitrate (si backend supporte) **Pas de problème identifié** --- ## React Query ### Cache ✅ **Bien configuré** : React Query v5 avec cache automatique - `staleTime` : défaut (0, toujours stale) - `cacheTime` : défaut (5min) **Recommandation** : - Augmenter `staleTime` pour les données peu changeantes (profils, tracks) - Configurer `staleTime: 5 * 60 * 1000` (5min) pour les tracks **Effort** : S (30min) --- ## WebSocket ### Reconnexion ✅ **Bien implémenté** : Backoff exponentiel dans `src/services/websocket.ts` (lignes 76-98) - Max 5 tentatives - Délai exponentiel : `3000 * 2^(attempt-1)` **Pas de problème identifié** --- # Issue List Priorisée (P0→P3) ## Format des issues Chaque issue suit le format : - **ID** : `WEB--###` - **Titre** - **Impact** (concret, scénarios) - **Preuve** : fichiers + symboles + extrait court - **Cause racine** - **Fix minimal** (patch-level, pas de refonte totale) - **Plan de validation** (tests + commande + cas limites) - **Effet de bord / risque de régression** - **Effort estimé** (S/M/L) + dépendances --- ## P0 — Build Breakers / Sécurité Critique ### WEB-P0-001 : Modules manquants (build cassé) **Impact** : Build TypeScript échoue, application ne compile pas **Preuve** : - `src/components/player/AudioPlayer.tsx:6` : `Cannot find module '@/hooks/use-toast'` - `src/components/player/AudioPlayer.tsx:2` : `Cannot find module '@/stores/player'` - `src/components/ui/optimized-image.tsx:2` : `Cannot find module '@/hooks/useIntersectionObserver'` - `src/components/ui/LazyComponent.tsx:39` : `Cannot find module '@/features/settings/pages/SettingsPage'` - Et 5+ autres modules manquants **Cause racine** : - Fichiers supprimés/déplacés sans mise à jour des imports - Exports manquants dans les modules **Fix minimal** : 1. Créer les modules manquants ou corriger les imports 2. Vérifier que tous les exports sont corrects **Plan de validation** : ```bash npm run typecheck # Doit passer sans erreur npm run build # Doit compiler ``` **Effet de bord** : Aucun (fix nécessaire pour build) **Effort** : M (2-3h) --- ### WEB-P0-002 : Tokens JWT en localStorage (XSS vulnérable) **Voir [Security Finding #1](#1-tokens-jwt-stockés-en-localstorage-xss-vulnérable)** **Effort** : M (2-4h) --- ### WEB-P0-003 : CSP non-strict (`unsafe-inline`, `unsafe-eval`) **Voir [Security Finding #2](#2-csp-non-strict-en-production-unsafe-inline-unsafe-eval)** **Effort** : S (1-2h) --- ### WEB-P0-004 : `dangerouslySetInnerHTML` sans sanitisation complète **Voir [Security Finding #3](#3-utilisation-de-dangerouslysetinnerhtml-sans-sanitisation-complète)** **Effort** : S (1h) --- ### WEB-P0-005 : CSRF Token non implémenté **Voir [Security Finding #4](#4-csrf-token-non-implémenté)** **Effort** : M (2-3h) --- ## P1 — Bugs fréquents / Dette bloquante ### WEB-P1-001 : Types Track/User incomplets (100+ erreurs TypeScript) **Impact** : Erreurs TypeScript partout, développement ralenti, bugs runtime possibles **Preuve** : - `src/components/player/AudioPlayer.tsx:90` : `Property 'url' does not exist on type 'Track'` - `src/components/player/AudioPlayer.tsx:179` : `Property 'cover_url' does not exist on type 'Track'` - `src/features/library/components/VirtualizedTrackList.tsx:56` : `Property 'album' does not exist on type 'Track'` - `src/features/chat/hooks/useChat.ts:9` : `Property 'userId' does not exist on type 'AuthState'` **Cause racine** : - Types `Track` et `User` dans `src/types/api.ts` ne correspondent pas à l'usage réel - Backend retourne plus de champs que définis dans les types **Fix minimal** : 1. Mettre à jour `src/types/api.ts` avec tous les champs utilisés 2. Vérifier la cohérence avec le backend Go **Plan de validation** : ```bash npm run typecheck # Doit passer sans erreur ``` **Effet de bord** : Aucun (correction de types) **Effort** : M (2-3h) --- ### WEB-P1-002 : React Query v5 incompatibilité (`onSuccess`, `keepPreviousData`) **Impact** : Warnings React Query, comportement incorrect possible **Preuve** : - `src/features/chat/pages/ChatPage.tsx:28` : `'onSuccess' does not exist` - `src/features/library/hooks/useMyTracks.ts:17` : `'keepPreviousData' does not exist` **Cause racine** : Migration React Query v4 → v5 incomplète **Fix minimal** : 1. Remplacer `onSuccess` par `useEffect` avec dépendance sur `data` 2. Remplacer `keepPreviousData` par `placeholderData: keepPreviousData` **Plan de validation** : ```bash npm run typecheck # Doit passer sans erreur npm test # Tests doivent passer ``` **Effet de bord** : Aucun (migration standard) **Effort** : S (1-2h) --- ### WEB-P1-003 : Tests échouent (usePlaylistTrack) **Impact** : CI/CD peut échouer, confiance réduite dans les tests **Preuve** : - `src/features/playlists/hooks/usePlaylistTrack.test.tsx` : 2 tests échouent - `useAddTrackToPlaylist` : argument manquant - `useRemoveTrackFromPlaylist` : fonction non exportée **Cause racine** : - Signature de fonction changée sans mise à jour des tests - Export manquant **Fix minimal** : 1. Corriger les signatures de fonction 2. Ajouter les exports manquants 3. Mettre à jour les tests **Plan de validation** : ```bash npm test -- usePlaylistTrack # Doit passer ``` **Effet de bord** : Aucun **Effort** : S (1h) --- ### WEB-P1-004 : Validation JWT côté client absente **Voir [Security Finding #6](#6-validation-jwt-côté-client-absente-expiration-non-vérifiée)** **Effort** : S (1h) --- ### WEB-P1-005 : Duplication de clients API (`apiService` vs `apiClient`) **Impact** : Confusion, maintenance difficile, bugs possibles **Preuve** : - `src/services/api.ts` : classe `ApiService` (singleton `apiService`) - `src/services/api/client.ts` : `apiClient` (Axios instance) - `src/lib/apiClient.ts` : autre `apiClient` (Axios instance) **Cause racine** : Refactoring incomplet, plusieurs implémentations coexistent **Fix minimal** : 1. Unifier en un seul client API 2. Migrer tous les usages vers le client unifié 3. Supprimer les clients dupliqués **Plan de validation** : ```bash grep -r "apiService\|apiClient" src/ # Vérifier qu'un seul client est utilisé npm run typecheck # Doit passer npm test # Tests doivent passer ``` **Effet de bord** : Risque de régression si migration incomplète **Effort** : M (3-4h) --- ### WEB-P1-006 : WebSocket token en query param **Voir [Security Finding #5](#5-websocket-token-en-query-param-exposé-dans-logsurls)** **Effort** : M (2-3h) --- ### WEB-P1-007 : Conversion IDs int64 → string manuelle (risque d'erreur) **Impact** : Bugs possibles si conversion oubliée, maintenance difficile **Preuve** : - `src/services/api.ts:339-344` : conversion manuelle `String(conv.id)` - `src/services/api.ts:361-374` : conversion manuelle `String(data.id)` **Cause racine** : Backend retourne `int64`, frontend attend `string` **Fix minimal** : 1. Créer un utilitaire `convertBackendId(id: number | string): string` 2. Utiliser partout où nécessaire 3. Ajouter des types stricts pour forcer la conversion **Plan de validation** : ```bash grep -r "String(.*\.id)" src/ # Vérifier que toutes les conversions utilisent l'utilitaire npm run typecheck # Doit passer ``` **Effet de bord** : Aucun **Effort** : S (1h) --- ## P2 — Qualité, maintenabilité, perf non critique ### WEB-P2-001 : 121 occurrences de `console.log/error/warn` (logs en production) **Impact** : Performance légère, exposition d'infos sensibles possible, pollution console **Preuve** : ```bash grep -r "console\." src/ | wc -l # 121 occurrences ``` **Cause racine** : Pas de logger structuré, logs de debug laissés **Fix minimal** : 1. Créer un logger wrapper (`src/utils/logger.ts`) 2. Remplacer tous les `console.*` par le logger 3. Désactiver les logs en production **Plan de validation** : ```bash grep -r "console\." src/ | wc -l # Doit être 0 npm run build && npm run preview # Vérifier que pas de logs en prod ``` **Effet de bord** : Aucun **Effort** : M (2-3h) --- ### WEB-P2-002 : Pas de rate limiting côté client **Voir [Security Finding #7](#7-pas-de-rate-limiting-côté-client)** **Effort** : S (1-2h) --- ### WEB-P2-003 : Re-renders inutiles (pas de `React.memo`) **Impact** : Performance dégradée sur les listes longues **Preuve** : - `src/components/data/Table.tsx:102` : `useCallback` avec dépendance manquante - Pas de `React.memo` sur `TrackList`, `ChatMessages`, etc. **Cause racine** : Optimisation non faite **Fix minimal** : 1. Ajouter `React.memo` sur les composants de liste 2. Corriger les dépendances de `useCallback` **Plan de validation** : - Profiler avec React DevTools - Vérifier que les re-renders sont réduits **Effet de bord** : Aucun **Effort** : M (2-3h) --- ### WEB-P2-004 : Pas de validation de taille de fichier upload **Voir [Security Finding #9](#9-pas-de-validation-de-taille-de-fichier-upload-côté-client)** **Effort** : S (1h) --- ### WEB-P2-005 : Pas de logging structuré **Voir [Observability - Logs structurés](#logs-structurés)** **Effort** : M (2-3h) --- ### WEB-P2-006 : Healthcheck basique (pas de vérification dépendances) **Voir [Observability - Healthcheck](#healthcheck)** **Effort** : S (1-2h) --- ### WEB-P2-007 : Pas de retry automatique (sauf refresh token) **Voir [Observability - Timeouts / Retries](#timeouts--retries)** **Effort** : M (2-3h) --- ### WEB-P2-008 : Bundle size non analysé **Voir [Performance - Bundle size](#1-bundle-size)** **Effort** : S (30min) --- ### WEB-P2-009 : CSP middleware inutile (code mort) **Voir [Security Finding #10](#10-csp-middleware-non-utilisé-créé-mais-jamais-appelé)** **Effort** : S (30min) --- ### WEB-P2-010 : Naming incohérent (`useAuthStore` vs `useAuth`, `apiService` vs `apiClient`) **Impact** : Confusion, maintenance difficile **Preuve** : - `src/features/auth/store/authStore.ts` : exporte `useAuthStore` (moved from stores/auth.ts) - `src/features/auth/hooks/useAuth.ts` : exporte `useAuth` (utilise `useAuthStore` en interne) - `src/services/api.ts` : `apiService` - `src/services/api/client.ts` : `apiClient` **Cause racine** : Refactoring incomplet, conventions non suivies **Fix minimal** : 1. Documenter les conventions de naming 2. Unifier progressivement (dans le cadre de P1-005) **Plan de validation** : - Revue de code pour vérifier cohérence **Effet de bord** : Aucun **Effort** : S (1h pour documentation) --- ## P3 — Cosmétique, refactors non urgents ### WEB-P3-001 : 30+ occurrences de `any` (types implicites) **Impact** : Perte de type safety, bugs possibles **Preuve** : ```bash grep -r ": any\|as any" src/ | wc -l # ~30 occurrences ``` **Cause racine** : Types non définis, migration TypeScript incomplète **Fix minimal** : 1. Remplacer progressivement les `any` par des types stricts 2. Utiliser `unknown` si type vraiment inconnu **Plan de validation** : ```bash npm run lint # Doit passer sans warnings @typescript-eslint/no-explicit-any ``` **Effet de bord** : Aucun **Effort** : L (4-6h, peut être fait progressivement) --- ### WEB-P3-002 : Pas de HSTS header **Impact** : Sécurité légèrement réduite (manque protection HTTPS forcé) **Preuve** : - `nginx.conf` : pas de header `Strict-Transport-Security` **Fix minimal** : ```nginx add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; ``` **Plan de validation** : - Vérifier avec `curl -I` que le header est présent **Effet de bord** : Aucun **Effort** : S (5min) --- ### WEB-P3-003 : `X-Frame-Options: SAMEORIGIN` au lieu de `DENY` **Impact** : Protection clickjacking partielle **Preuve** : - `nginx.conf:14` : `X-Frame-Options "SAMEORIGIN"` **Fix minimal** : ```nginx add_header X-Frame-Options "DENY" always; ``` **Plan de validation** : - Vérifier avec `curl -I` **Effet de bord** : Aucun (pas d'iframe dans l'app) **Effort** : S (5min) --- ### WEB-P3-004 : Pas de validation de format de fichier upload **Voir [Security - Autres problèmes P2](#p2--moyenne-priorité)** **Effort** : S (1h) --- ### WEB-P3-005 : TODO/FIXME dans le code (38 occurrences) **Impact** : Dette technique, fonctionnalités incomplètes **Preuve** : ```bash grep -ri "TODO\|FIXME\|XXX\|HACK" src/ | wc -l # 38 occurrences ``` **Exemples** : - `src/features/chat/hooks/useChat.ts:114` : `// TODO: Add fetchHistory function` - `src/features/library/components/LibraryManager.tsx:247` : `isLiked={() => false} // Todo` **Fix minimal** : 1. Créer des issues GitHub pour chaque TODO 2. Supprimer les TODOs résolus 3. Documenter les TODOs non urgents **Plan de validation** : - Revue manuelle des TODOs **Effet de bord** : Aucun **Effort** : S (1h pour trier et documenter) --- # Execution Plan ## Checklist P0 (ordre strict) ### Phase 1 : Build Fixes (Critique — 2-3h) 1. ✅ **WEB-P0-001** : Créer/corriger les modules manquants - Créer `src/hooks/use-toast.ts` (ou corriger import) - Créer `src/stores/player.ts` (ou corriger import) - Créer `src/hooks/useIntersectionObserver.ts` (ou corriger import) - Corriger les imports dans `src/components/ui/LazyComponent.tsx` - Valider : `npm run typecheck` passe 2. ✅ **WEB-P1-001** : Corriger les types Track/User - Mettre à jour `src/types/api.ts` avec tous les champs - Valider : `npm run typecheck` passe ### Phase 2 : Sécurité Critique (4-6h) 3. ✅ **WEB-P0-004** : Utiliser DOMPurify pour sanitisation - Remplacer `sanitizeChatMessage` par `DOMPurify.sanitize()` - Valider : Test XSS avec `` 4. ✅ **WEB-P0-003** : Renforcer CSP (supprimer `unsafe-eval`) - Modifier `vite.config.ts` pour production - Utiliser nonces pour scripts inline - Valider : Build prod, vérifier CSP header 5. ✅ **WEB-P0-002** : Migrer tokens vers httpOnly cookies (nécessite backend) - **Option alternative** : Utiliser `sessionStorage` + rotation fréquente - Valider : Test XSS ne peut plus voler tokens 6. ✅ **WEB-P0-005** : Implémenter CSRF token (nécessite backend) - Implémenter `refreshCsrfToken()` dans `src/services/csrf.ts` - Ajouter header dans intercepteurs Axios - Valider : Test CSRF échoue sans token ### Phase 3 : Tests (1h) 7. ✅ **WEB-P1-003** : Corriger tests usePlaylistTrack - Corriger signatures et exports - Valider : `npm test -- usePlaylistTrack` passe --- ## Checklist P1 (par lots cohérents) ### Lot 1 : React Query v5 Migration (1-2h) 8. ✅ **WEB-P1-002** : Migrer `onSuccess` → `useEffect` - Remplacer dans `src/features/chat/pages/ChatPage.tsx` - Remplacer `keepPreviousData` → `placeholderData` - Valider : `npm run typecheck` + `npm test` ### Lot 2 : Unification Clients API (3-4h) 9. ✅ **WEB-P1-005** : Unifier `apiService` et `apiClient` - Choisir un client (recommandé : `apiClient` de `src/services/api/client.ts`) - Migrer tous les usages - Supprimer `src/services/api.ts` (ou le garder comme wrapper) - Valider : `npm run typecheck` + `npm test` + tests manuels ### Lot 3 : JWT & WebSocket (3-4h) 10. ✅ **WEB-P1-004** : Validation JWT expiration côté client - Décoder JWT et vérifier `exp` - Refresh automatique si < 5min - Valider : Test avec token expiré 11. ✅ **WEB-P1-006** : WebSocket token en header (nécessite backend) - **Option alternative** : Utiliser token temporaire (one-time) - Valider : Token n'apparaît pas dans logs ### Lot 4 : Utilitaires (1h) 12. ✅ **WEB-P1-007** : Créer utilitaire conversion IDs - Créer `src/utils/id-converter.ts` - Remplacer toutes les conversions manuelles - Valider : `grep -r "String(.*\.id)" src/` retourne 0 --- ## Quick Wins (≤ 1h chacun) ### Sécurité - ✅ **WEB-P2-009** : Supprimer CSP middleware inutile (30min) - ✅ **WEB-P3-002** : Ajouter HSTS header (5min) - ✅ **WEB-P3-003** : Changer `X-Frame-Options` à `DENY` (5min) - ✅ **WEB-P2-004** : Validation taille fichier upload (1h) ### Qualité - ✅ **WEB-P2-008** : Analyser bundle size (30min) - ✅ **WEB-P3-005** : Trier et documenter TODOs (1h) --- ## Tests à ajouter en priorité ### P0 — Critiques 1. **Test XSS** : Injecter `` dans chat, vérifier qu'il ne s'exécute pas 2. **Test CSRF** : Requête POST depuis site externe, vérifier rejet 3. **Test token expiration** : Vérifier refresh automatique ### P1 — Importants 4. **Test rate limiting** : Envoyer 100 requêtes, vérifier limitation 5. **Test upload taille max** : Tenter upload > 100MB, vérifier rejet 6. **Test WebSocket reconnexion** : Simuler déconnexion, vérifier reconnexion --- ## PR Plan ### PR 1 : Build Fixes (P0-001, P1-001) **Titre** : `fix: resolve TypeScript build errors (missing modules, incomplete types)` **Fichiers** : - Créer modules manquants - Mettre à jour `src/types/api.ts` **Tests** : `npm run typecheck` passe ### PR 2 : Security - XSS & CSP (P0-003, P0-004) **Titre** : `security: improve XSS protection (DOMPurify, strict CSP)` **Fichiers** : - `src/features/chat/components/ChatMessages.tsx` - `src/features/chat/components/VirtualizedChatMessages.tsx` - `vite.config.ts` **Tests** : Test XSS manuel ### PR 3 : Security - Tokens & CSRF (P0-002, P0-005) **Titre** : `security: migrate tokens to httpOnly cookies, implement CSRF protection` **Fichiers** : - `src/services/api/auth.ts` - `src/services/csrf.ts` - `src/services/api/client.ts` **Tests** : Tests CSRF, test XSS token theft **Note** : Nécessite coordination backend ### PR 4 : React Query v5 Migration (P1-002) **Titre** : `chore: migrate to React Query v5 API (remove onSuccess, keepPreviousData)` **Fichiers** : - `src/features/chat/pages/ChatPage.tsx` - `src/features/library/hooks/useMyTracks.ts` **Tests** : `npm test` passe ### PR 5 : Unify API Clients (P1-005) **Titre** : `refactor: unify API clients (remove duplication)` **Fichiers** : - `src/services/api.ts` (supprimer ou wrapper) - `src/services/api/client.ts` (garder) - Tous les fichiers utilisant `apiService` **Tests** : Tests complets + tests manuels ### PR 6 : JWT & WebSocket Security (P1-004, P1-006) **Titre** : `security: add JWT expiration validation, move WebSocket token to header` **Fichiers** : - `src/services/api/client.ts` - `src/services/websocket.ts` - `src/utils/token-manager.ts` **Tests** : Tests expiration, tests WebSocket **Note** : WebSocket nécessite backend ### PR 7 : Utilities & Tests (P1-007, P1-003) **Titre** : `fix: add ID converter utility, fix usePlaylistTrack tests` **Fichiers** : - `src/utils/id-converter.ts` - `src/services/api.ts` (utiliser converter) - `src/features/playlists/hooks/usePlaylistTrack.test.tsx` **Tests** : Tests unitaires ### PR 8 : Quick Wins (P2-009, P3-002, P3-003, P2-004, P2-008, P3-005) **Titre** : `chore: quick wins (remove dead code, add security headers, validate uploads, analyze bundle, document TODOs)` **Fichiers** : - `src/utils/csp.ts` (supprimer middleware) - `nginx.conf` (ajouter headers) - `src/features/library/components/UploadModal.tsx` (validation taille) - `package.json` (script bundle analysis) - Documentation TODOs **Tests** : Tests manuels --- ## Estimation totale - **P0** : 8-12h (build fixes + sécurité critique) - **P1** : 8-12h (bugs fréquents, dette bloquante) - **P2** : 6-10h (qualité, maintenabilité) - **P3** : 2-4h (cosmétique) **Total** : **24-38h** de travail (3-5 jours développeur) --- ## Dépendances externes ### Backend Go (`veza-backend-api`) - Support httpOnly cookies pour tokens (P0-002) - Endpoint CSRF token (P0-005) - Validation CSRF sur toutes les requêtes POST/PUT/DELETE (P0-005) ### Chat Server Rust (`veza-chat-server`) - Support token JWT en header au lieu de query param (P1-006) ### Stream Server Rust (`veza-stream-server`) - Aucune dépendance identifiée --- ## Notes finales ### Points positifs ✅ Architecture feature-based claire ✅ Tests unitaires nombreux (189 fichiers) ✅ Utilisation de Zod pour validation ✅ ErrorBoundary React ✅ Upload par chunks pour gros fichiers ✅ WebSocket avec reconnexion intelligente ✅ PWA support (service worker) ✅ i18n (français/anglais) ### Points à surveiller ⚠️ Duplication de code (clients API) ⚠️ Logs console partout (121 occurrences) ⚠️ Types incomplets (Track/User) ⚠️ Sécurité tokens (localStorage) ### Recommandations long terme 1. **Centraliser la gestion d'erreurs** : Créer un service d'erreurs global 2. **Logger structuré** : Implémenter un logger avec niveaux et corrélation 3. **Métriques** : Ajouter des métriques custom (erreurs, latence, etc.) 4. **Documentation** : Documenter les conventions de code et les patterns 5. **CI/CD** : Ajouter des checks automatiques (typecheck, lint, tests) dans CI --- **Fin du rapport d'audit**