Phase 1:
- S0: Fix open redirect (safeNavigate), delete AuthContext/legacy auth, encrypt API keys, gitignore .env files
- S1: Split client.ts god object into 5 modules, unify toast system, delete unused Sidebar
- S2: Add glass button variant, migrate 32 z-index to SUMI tokens, fix card dark mode
- S3: Skip nav link, aria-hidden on icons, focus-visible ring fixes, alt attrs, aria-live regions
- S4: React.memo on list items, fix key={index}, loading=lazy on images
- S5: Branded loading screen, page transitions respect reduced-motion, LikeButton micro-interaction, i18n sidebar/header
Phase 2 Sprint 6:
- Wire Tailwind shadow utilities to SUMI tokens in @theme block (fixes 50+ files)
- Define shadow-card/shadow-card-hover tokens
- Remove dark:shadow-none workarounds from card.tsx (SUMI handles per-theme shadows)
Co-authored-by: Cursor <cursoragent@cursor.com>
6.7 KiB
Phase F — Sécurité Frontend
F1. Secrets et données sensibles
Variables VITE_* exposées au bundle
| Variable | Contenu | Sensible ? |
|---|---|---|
VITE_API_URL |
URL API backend | ❌ Public |
VITE_WS_URL |
URL WebSocket | ❌ Public |
VITE_STREAM_URL |
URL streaming | ❌ Public |
VITE_UPLOAD_URL |
URL upload | ❌ Public |
VITE_APP_NAME |
Nom de l'app | ❌ Public |
VITE_DEBUG |
Flag debug | ❌ Non sensible |
VITE_USE_MSW |
Flag MSW | ❌ Dev only |
VITE_FCM_VAPID_KEY |
Clé push notifications | ⚠️ Public par design (VAPID) |
VITE_FEATURE_* |
Feature flags | ❌ Non sensible |
Verdict : Aucune clé API secrète exposée côté client. ✅
Fichiers .env
.env.local(450B) : ContientVITE_DOMAIN,VITE_API_URL,VITE_WS_URL,VITE_STREAM_URL— pas de secrets ✅.env.production(1.8KB) : Contient URLs et config — pas de secrets ✅.env.example(2.2KB) : Template
Attention : .env.local et .env.production sont versionnés (présents dans le repo). Le .gitignore ne semble pas exclure .env.local. Même si le contenu n'est pas sensible actuellement, c'est un risque si quelqu'un ajoute un secret à l'avenir.
Stockage JWT
- httpOnly cookies : Les tokens JWT sont gérés côté backend via cookies httpOnly ✅
TokenStorage.getAccessToken()retournenullen mode cookie — pas d'accès JS aux tokens ✅tokenStorage.tsest un wrapper de compatibilité, pas de stockage réel en localStorage ✅
F2. XSS
dangerouslySetInnerHTML
| Fichier | Ligne | Source des données | Sanitization |
|---|---|---|---|
features/chat/components/ChatMessages.tsx |
145-147 | message.content (backend) |
✅ sanitizeChatMessage() via DOMPurify |
features/chat/components/virtualized-chat-messages/VirtualizedChatMessageItem.tsx |
58-60 | message.content (backend) |
✅ sanitizeChatMessage() via DOMPurify |
Sanitization : utils/sanitize.ts (429L) utilise DOMPurify avec :
- Allowlist stricte de tags HTML [sanitize.ts]
- Allowlist d'attributs [sanitize.ts]
- URL schemes limitées (http, https, mailto) [sanitize.ts]
javascript:protocol filtré ✅
Verdict XSS : ✅ Les deux usages de dangerouslySetInnerHTML sont correctement sanitizés.
Autres vecteurs XSS
eval()/new Function(): 0 occurrence ✅- Template literals dans le DOM : Non détecté ✅
document.write: 0 occurrence ✅
F3. Stockage client
Données en localStorage
| Clé | Données | Sensible ? | Expiration |
|---|---|---|---|
ui-storage |
theme, language, sidebarOpen | ❌ | Persist |
veza-cart-storage |
Items du panier | ❌ | Persist |
auth-storage |
isAuthenticated (boolean) |
❌ | Persist |
rememberedEmail |
Email utilisateur | ⚠️ PII | Persist |
veza_offline_queue |
Requêtes en attente | ⚠️ Peut contenir des données | Nettoyé |
PENDING_ANALYTICS_STORAGE_KEY |
Analytics payload | ❌ | Nettoyé |
feature-highlight-* |
Dismissal flags | ❌ | Persist |
pwa-install-dismissed |
Flag | ❌ | Persist |
veza_wrong_server_shown |
Flag toast | ❌ | Persist |
| Developer API keys | Clés API | 🔴 OUI | Persist |
Problème critique : services/developerService.ts stocke des clés API dans localStorage. Même si ce sont des clés de développeur (probablement des API keys publiques), le stockage en localStorage les expose à toute extension de navigateur ou code tiers injecté. Recommandation : déléguer la gestion au backend.
sessionStorage
- Pas d'usage sensible détecté ✅
F4. Dépendances
Vulnérabilités connues
[DONNÉES INSUFFISANTES — nécessite npm audit sur la machine]
Dépendances à risque
| Dépendance | Version | Risque | Usage |
|---|---|---|---|
dompurify |
3.3.x | ✅ Activement maintenu | Sanitization |
axios |
1.13.x | ✅ Récent | HTTP |
swagger-ui-react |
5.31.x | ⚠️ Exposé en production ? | Dev tools |
hls.js |
1.6.x | ✅ Maintenu | Streaming |
Attention : swagger-ui-react et swagger-ui-dist sont en dependencies (pas devDependencies). Si le composant SwaggerUI est accessible en production, cela expose la documentation API. Vérifier que la route /developer est bien protégée et gated.
F5. Autres vecteurs
Open redirect
| Fichier | Risque | Détail |
|---|---|---|
features/playlists/hooks/usePlaylistNotifications.ts:203,219,235,251 |
🔴 HAUT | window.location.href = notification.link! — URL provenant du backend, pas de validation. Si un attaquant compromet les notifications, il peut rediriger vers un site malveillant. |
Autres window.location.href |
✅ Sûr | Tous vers des chemins statiques (/login, /marketplace, etc.) |
CORS / CSP
- CORS : Géré côté backend. Le proxy Vite en dev élimine les problèmes CORS [vite.config.ts:63-76] ✅
- CSP : [DONNÉES INSUFFISANTES — nécessite inspection des headers serveur]
Prototype pollution
- Pas d'usage de
lodash.mergeou similaire détecté ✅ immer(10.x) utilisé pour l'immutabilité — protège contre la mutation directe ✅
Classement des vulnérabilités
| Gravité | Vulnérabilité | Fichier:ligne | Exploitabilité | Correction urgence |
|---|---|---|---|---|
| 🔴 CRITIQUE | Open redirect via notification.link | usePlaylistNotifications.ts:203,219,235,251 |
Moyenne (nécessite compromission backend/notifications) | Immédiate — valider l'URL (same-origin ou allowlist) |
| 🟠 HAUTE | Clés API en localStorage | developerService.ts:33 |
Faible (nécessite accès au navigateur) | Court terme — migrer vers backend |
| 🟡 MOYENNE | .env.local versionné |
.env.local |
Faible (pas de secrets actuels) | Ajouter .env.local au .gitignore |
| 🟡 MOYENNE | swagger-ui en production | package.json (dependencies) |
Faible (route protégée) | Déplacer en devDependencies si non nécessaire en prod |
| 🟢 BASSE | rememberedEmail en localStorage |
LoginPage.tsx:115 |
Très faible (PII minimal) | Acceptable avec notice RGPD |
Score Sécurité implicite
Ce score n'est pas dans le tableau principal car la pondération est ×1.5, mais les observations sont globalement positives :
- ✅ httpOnly cookies pour JWT
- ✅ CSRF protection
- ✅ DOMPurify pour
dangerouslySetInnerHTML - ✅ Zod validation sur les réponses API
- ✅ Pas de
eval()ni secrets exposés - ❌ Open redirect dans usePlaylistNotifications
- ❌ Clés API en localStorage
Score Sécurité : 7/10