veza/apps/web/dev_audit/frontend/06_security_frontend.md
senke 5f88c56113 fix: UI remediation Phase 1 (S0-S5) + Phase 2 Sprint 6 shadow system
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>
2026-02-12 10:13:44 +01:00

6.7 KiB
Raw Permalink Blame History

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) : Contient VITE_DOMAIN, VITE_API_URL, VITE_WS_URL, VITE_STREAM_URLpas 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() retourne null en mode cookie — pas d'accès JS aux tokens
  • tokenStorage.ts est 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.merge ou 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