1811 lines
51 KiB
Markdown
1811 lines
51 KiB
Markdown
# 🔍 AUDIT TECHNIQUE EXHAUSTIF — Module Veza Mobile
|
|
|
|
**Date**: 2024-12-19
|
|
**Auditeur**: Auto (Cursor AI)
|
|
**Module**: `veza-mobile`
|
|
**Type**: Application React Native / Expo
|
|
**Statut**: ⚠️ **CRITIQUE** — Module non fonctionnel, nombreuses dépendances manquantes
|
|
|
|
---
|
|
|
|
## 📋 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
|
|
|
|
**Veza Mobile** est une application mobile React Native (Expo) destinée à être l'interface utilisateur mobile de la plateforme Veza. Elle permet de :
|
|
- Gérer et visualiser les features de la plateforme
|
|
- Consulter les analytics en temps réel
|
|
- Gérer les médias audio
|
|
- Communiquer via chat
|
|
- Gérer l'authentification utilisateur (MFA supporté)
|
|
- Fonctionner en mode offline avec synchronisation
|
|
|
|
### Entrées / Sorties
|
|
|
|
#### APIs Exposées
|
|
- **Aucune API exposée** (client uniquement)
|
|
- Communication avec backend via :
|
|
- HTTP REST (endpoints non définis dans le code)
|
|
- WebSocket (chat, streaming — non implémenté)
|
|
- Authentification JWT (tokens stockés dans Redux persist)
|
|
|
|
#### Formats de Données
|
|
- **Redux State**: JSON sérialisé dans AsyncStorage
|
|
- **API**: JSON (assumé, non documenté)
|
|
- **WebSocket**: JSON (assumé, non implémenté)
|
|
|
|
### Dépendances Internes
|
|
|
|
**Aucune dépendance interne identifiée** — module isolé du monorepo.
|
|
|
|
### Dépendances Externes
|
|
|
|
#### Runtime
|
|
- **React Native** (via Expo)
|
|
- **React Navigation** (`@react-navigation/native`, `@react-navigation/bottom-tabs`, `@react-navigation/stack`)
|
|
- **Redux Toolkit** (`@reduxjs/toolkit`)
|
|
- **Redux Persist** (`redux-persist`)
|
|
- **AsyncStorage** (`@react-native-async-storage/async-storage`)
|
|
- **Expo** (`expo-status-bar`, `@expo/vector-icons`)
|
|
- **Sentry** (`@sentry/react`, `@sentry/tracing`) — **⚠️ INCOMPATIBLE avec React Native**
|
|
|
|
#### Tests
|
|
- **Testing Library** (`@testing-library/react`, `@testing-library/jest-dom`)
|
|
- **Jest**
|
|
- **MSW** (Mock Service Worker — référencé mais non présent)
|
|
|
|
### Points d'Intégration
|
|
|
|
#### Backend API
|
|
- **URL**: Non configurée (variables d'environnement manquantes)
|
|
- **Auth**: JWT dans header `Authorization` (assumé)
|
|
- **CORS**: Non configuré
|
|
- **Endpoints attendus** (inférés du code) :
|
|
- `POST /api/auth/login`
|
|
- `POST /api/auth/register`
|
|
- `GET /api/features`
|
|
- `GET /api/analytics`
|
|
- `GET /api/media`
|
|
- `GET /api/chat`
|
|
|
|
#### Schéma de Données
|
|
- **User ID**: `string` (UUID attendu)
|
|
- **Feature ID**: `string`
|
|
- **Token**: `string` (JWT)
|
|
- **Refresh Token**: `string`
|
|
|
|
### Runbook Minimal
|
|
|
|
#### Prérequis
|
|
```bash
|
|
# Aucun package.json trouvé — dépendances inconnues
|
|
# Nécessite :
|
|
- Node.js 18+
|
|
- npm/yarn/pnpm
|
|
- Expo CLI
|
|
- iOS Simulator / Android Emulator (pour tests)
|
|
```
|
|
|
|
#### Build
|
|
```bash
|
|
# ❌ IMPOSSIBLE — package.json manquant
|
|
# Attendu :
|
|
npm install
|
|
npx expo start
|
|
```
|
|
|
|
#### Run Dev
|
|
```bash
|
|
# ❌ IMPOSSIBLE — configuration manquante
|
|
# Attendu :
|
|
npx expo start --dev-client
|
|
```
|
|
|
|
#### Tests
|
|
```bash
|
|
# ❌ IMPOSSIBLE — configuration manquante
|
|
# Attendu :
|
|
npm test
|
|
```
|
|
|
|
#### Production Build
|
|
```bash
|
|
# ❌ IMPOSSIBLE — configuration manquante
|
|
# Attendu :
|
|
eas build --platform ios
|
|
eas build --platform android
|
|
```
|
|
|
|
---
|
|
|
|
## 🏥 HEALTH/BUILD/TEST STATUS
|
|
|
|
### État Actuel : 🔴 **CRITIQUE — NON FONCTIONNEL**
|
|
|
|
### Build Status
|
|
|
|
| Composant | Status | Erreurs |
|
|
|-----------|--------|---------|
|
|
| **Configuration** | ❌ **ABSENT** | `package.json`, `tsconfig.json`, `app.json`, `babel.config.js` manquants |
|
|
| **Dépendances** | ❌ **INCONNUES** | Aucun lockfile, versions non définies |
|
|
| **TypeScript** | ⚠️ **PARTIEL** | Fichiers `.ts`/`.tsx` présents mais config absente |
|
|
| **Expo** | ⚠️ **PARTIEL** | Imports Expo présents mais `app.json` manquant |
|
|
|
|
### Test Status
|
|
|
|
| Aspect | Status | Détails |
|
|
|--------|--------|---------|
|
|
| **Configuration Tests** | ⚠️ **PARTIEL** | `setupTests.ts` présent mais incomplet |
|
|
| **Tests Unitaires** | ⚠️ **PARTIEL** | 1 test présent (`Dashboard.test.tsx`) mais dépendances manquantes |
|
|
| **Couverture** | ❌ **INCONNUE** | Aucun rapport de couverture |
|
|
| **MSW** | ❌ **MANQUANT** | `./mocks/server` référencé mais absent |
|
|
| **Fiabilité** | ❌ **INCONNUE** | Tests non exécutables |
|
|
|
|
### Linter Status
|
|
|
|
| Outil | Status | Erreurs |
|
|
|-------|--------|---------|
|
|
| **ESLint** | ❌ **NON CONFIGURÉ** | Aucun `.eslintrc` trouvé |
|
|
| **TypeScript** | ⚠️ **PARTIEL** | Pas de `tsconfig.json` pour validation |
|
|
| **Prettier** | ❌ **NON CONFIGURÉ** | Aucun `.prettierrc` trouvé |
|
|
|
|
### Erreurs de Compilation Identifiées
|
|
|
|
#### 1. Imports Manquants (P0)
|
|
|
|
**Fichier**: `App.tsx`
|
|
```12:18:App.tsx
|
|
import FeaturesScreen from './src/screens/FeaturesScreen';
|
|
import AnalyticsScreen from './src/screens/AnalyticsScreen';
|
|
import MediaScreen from './src/screens/MediaScreen';
|
|
import ChatScreen from './src/screens/ChatScreen';
|
|
import SettingsScreen from './src/screens/SettingsScreen';
|
|
import LoginScreen from './src/screens/auth/LoginScreen';
|
|
import RegisterScreen from './src/screens/auth/RegisterScreen';
|
|
```
|
|
|
|
**Erreur**: 7 screens importés mais absents du filesystem.
|
|
|
|
#### 2. Redux Slices Manquants (P0)
|
|
|
|
**Fichier**: `src/store/store.ts`
|
|
```8:12:src/store/store.ts
|
|
import featuresReducer from './slices/featuresSlice';
|
|
import analyticsReducer from './slices/analyticsSlice';
|
|
import mediaReducer from './slices/mediaSlice';
|
|
import chatReducer from './slices/chatSlice';
|
|
import uiReducer from './slices/uiSlice';
|
|
```
|
|
|
|
**Erreur**: 5 reducers importés mais absents.
|
|
|
|
#### 3. Incompatibilité React Native / Web (P0)
|
|
|
|
**Fichiers**:
|
|
- `src/components/UI/Button.tsx` — utilise `<button>` et classes Tailwind (web)
|
|
- `src/components/UI/Card.tsx` — utilise `<div>` et classes Tailwind (web)
|
|
- `src/components/UI/Modal.tsx` — utilise `react-dom` `createPortal` (web)
|
|
|
|
**Erreur**: Composants web dans une app React Native.
|
|
|
|
#### 4. Sentry Incompatible (P0)
|
|
|
|
**Fichier**: `src/services/monitoring.ts`
|
|
```1:2:src/services/monitoring.ts
|
|
import * as Sentry from '@sentry/react';
|
|
import { BrowserTracing } from '@sentry/tracing';
|
|
```
|
|
|
|
**Erreur**: `@sentry/react` et `BrowserTracing` sont pour React web, pas React Native. Devrait utiliser `@sentry/react-native`.
|
|
|
|
#### 5. Test Setup Incomplet (P1)
|
|
|
|
**Fichier**: `src/setupTests.ts`
|
|
```3:3:src/setupTests.ts
|
|
import { server } from './mocks/server';
|
|
```
|
|
|
|
**Erreur**: `./mocks/server` n'existe pas.
|
|
|
|
#### 6. Test Import Incorrect (P1)
|
|
|
|
**Fichier**: `src/pages/Dashboard/__tests__/Dashboard.test.tsx`
|
|
```5:5:src/pages/Dashboard/__tests__/Dashboard.test.tsx
|
|
import Dashboard from '../Dashboard';
|
|
```
|
|
|
|
**Erreur**: Importe `Dashboard` mais le fichier s'appelle `DashboardScreen.tsx` et est dans `src/screens/`.
|
|
|
|
### Gestion des Erreurs
|
|
|
|
| Aspect | Status | Problèmes |
|
|
|--------|--------|-----------|
|
|
| **Error Boundaries** | ❌ **ABSENT** | Aucun `ErrorBoundary` React |
|
|
| **Try/Catch** | ⚠️ **PARTIEL** | Quelques try/catch mais pas systématique |
|
|
| **Error Logging** | ⚠️ **PARTIEL** | Sentry configuré mais incompatible |
|
|
| **User Feedback** | ⚠️ **PARTIEL** | États d'erreur dans Redux mais pas d'UI d'erreur |
|
|
|
|
### Conventions de Code
|
|
|
|
| Convention | Status | Problèmes |
|
|
|------------|--------|-----------|
|
|
| **Naming** | ✅ **COHÉRENT** | PascalCase pour composants, camelCase pour fonctions |
|
|
| **Structure** | ⚠️ **PARTIEL** | Mélange `pages/` et `screens/` |
|
|
| **Séparation Couches** | ✅ **BON** | Store, services, components séparés |
|
|
| **Types** | ✅ **BON** | Interfaces TypeScript définies |
|
|
|
|
---
|
|
|
|
## 🔒 SECURITY FINDINGS
|
|
|
|
### Top 10 Risques Sécurité
|
|
|
|
#### 1. **P0 — Tokens JWT Stockés en Clair dans AsyncStorage**
|
|
|
|
**Fichier**: `src/store/store.ts`
|
|
```16:20:src/store/store.ts
|
|
const persistConfig = {
|
|
key: 'root',
|
|
storage: AsyncStorage,
|
|
whitelist: ['auth', 'offline'], // Only persist auth and offline data
|
|
};
|
|
```
|
|
|
|
**Risque**:
|
|
- Tokens JWT stockés en clair dans AsyncStorage (lisible par toute app avec accès root)
|
|
- Pas de chiffrement des tokens sensibles
|
|
- Refresh tokens également en clair
|
|
|
|
**Impact**:
|
|
- Vol de session si device compromis
|
|
- Accès non autorisé aux données utilisateur
|
|
- Violation RGPD si tokens contiennent PII
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
import EncryptedStorage from 'react-native-encrypted-storage';
|
|
|
|
const persistConfig = {
|
|
key: 'root',
|
|
storage: {
|
|
getItem: async (key: string) => {
|
|
const encrypted = await EncryptedStorage.getItem(key);
|
|
return encrypted ? JSON.parse(encrypted) : null;
|
|
},
|
|
setItem: async (key: string, value: any) => {
|
|
await EncryptedStorage.setItem(key, JSON.stringify(value));
|
|
},
|
|
removeItem: async (key: string) => {
|
|
await EncryptedStorage.removeItem(key);
|
|
},
|
|
},
|
|
whitelist: ['auth', 'offline'],
|
|
};
|
|
```
|
|
|
|
**Validation**:
|
|
- Test unitaire : vérifier que tokens sont chiffrés
|
|
- Test d'intégration : vérifier que tokens ne sont pas lisibles en clair
|
|
- Audit manuel : inspecter AsyncStorage avec React Native Debugger
|
|
|
|
**Effet de Bord**: Nécessite `react-native-encrypted-storage` (dépendance native)
|
|
|
|
---
|
|
|
|
#### 2. **P0 — Pas de Validation JWT (Expiration, Signature)**
|
|
|
|
**Fichier**: `src/store/slices/authSlice.ts`
|
|
```44:50:src/store/slices/authSlice.ts
|
|
loginSuccess: (state, action: PayloadAction<{ user: User; token: string; refreshToken: string }>) => {
|
|
state.isLoading = false;
|
|
state.isAuthenticated = true;
|
|
state.user = action.payload.user;
|
|
state.token = action.payload.token;
|
|
state.refreshToken = action.payload.refreshToken;
|
|
state.error = null;
|
|
},
|
|
```
|
|
|
|
**Risque**:
|
|
- Aucune validation de l'expiration du token
|
|
- Aucune vérification de la signature
|
|
- Tokens expirés peuvent être utilisés indéfiniment
|
|
- Pas de refresh automatique
|
|
|
|
**Impact**:
|
|
- Sessions infinies même après expiration
|
|
- Utilisation de tokens compromis non détectée
|
|
- Violation de sécurité si backend invalide le token
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
import jwtDecode from 'jwt-decode';
|
|
|
|
interface JWTPayload {
|
|
exp: number;
|
|
iat: number;
|
|
sub: string;
|
|
// ... autres claims
|
|
}
|
|
|
|
const validateToken = (token: string): boolean => {
|
|
try {
|
|
const decoded = jwtDecode<JWTPayload>(token);
|
|
const now = Math.floor(Date.now() / 1000);
|
|
return decoded.exp > now;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Dans le reducer
|
|
loginSuccess: (state, action) => {
|
|
if (!validateToken(action.payload.token)) {
|
|
state.error = 'Token invalide ou expiré';
|
|
return;
|
|
}
|
|
// ... reste du code
|
|
},
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : token expiré doit être rejeté
|
|
- Test : token invalide doit être rejeté
|
|
- Test : refresh automatique si token expire bientôt
|
|
|
|
**Effet de Bord**: Nécessite `jwt-decode` ou `jsonwebtoken`
|
|
|
|
---
|
|
|
|
#### 3. **P0 — Authentification Hardcodée à `false`**
|
|
|
|
**Fichier**: `App.tsx`
|
|
```131:132:App.tsx
|
|
// TODO: Check authentication state
|
|
const isAuthenticated = false; // This should come from auth state
|
|
```
|
|
|
|
**Risque**:
|
|
- L'utilisateur ne peut jamais s'authentifier
|
|
- Toutes les routes protégées sont accessibles sans auth
|
|
- Bypass complet de l'authentification
|
|
|
|
**Impact**:
|
|
- **CRITIQUE** : Accès non autorisé à toutes les fonctionnalités
|
|
- Violation de sécurité majeure
|
|
- Données utilisateur exposées
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
import { useSelector } from 'react-redux';
|
|
import { RootState } from './src/store/store';
|
|
|
|
const RootNavigator = () => {
|
|
const isAuthenticated = useSelector((state: RootState) => state.auth.isAuthenticated);
|
|
// ... reste
|
|
};
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : utilisateur non authentifié → AuthNavigator
|
|
- Test : utilisateur authentifié → MainTabNavigator
|
|
- Test E2E : flow complet login → dashboard
|
|
|
|
**Effet de Bord**: Aucun, fix immédiat
|
|
|
|
---
|
|
|
|
#### 4. **P1 — Pas de Protection CSRF**
|
|
|
|
**Risque**:
|
|
- Aucun token CSRF dans les requêtes API
|
|
- Vulnérable aux attaques CSRF si backend ne vérifie pas l'origine
|
|
|
|
**Impact**:
|
|
- Actions malveillantes possibles via requêtes cross-origin
|
|
- Modification de données utilisateur sans consentement
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Dans service API
|
|
const getCSRFToken = async () => {
|
|
const token = await AsyncStorage.getItem('csrf_token');
|
|
if (!token) {
|
|
// Fetch from backend
|
|
const response = await fetch('/api/csrf-token');
|
|
const { token } = await response.json();
|
|
await AsyncStorage.setItem('csrf_token', token);
|
|
return token;
|
|
}
|
|
return token;
|
|
};
|
|
|
|
// Ajouter dans headers
|
|
headers: {
|
|
'X-CSRF-Token': await getCSRFToken(),
|
|
}
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : vérifier présence header CSRF
|
|
- Test : vérifier rotation du token
|
|
- Audit : vérifier validation côté backend
|
|
|
|
**Effet de Bord**: Nécessite support backend
|
|
|
|
---
|
|
|
|
#### 5. **P1 — Variables d'Environnement Manquantes**
|
|
|
|
**Fichier**: `src/services/monitoring.ts`
|
|
```5:5:src/services/monitoring.ts
|
|
const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || '';
|
|
```
|
|
|
|
**Risque**:
|
|
- Utilise `REACT_APP_*` (convention Create React App) au lieu de `EXPO_PUBLIC_*` (Expo)
|
|
- Variables d'environnement non définies
|
|
- Pas de `.env.example` pour documenter
|
|
|
|
**Impact**:
|
|
- Configuration incorrecte en production
|
|
- Secrets potentiellement exposés si mal configurés
|
|
- Debugging difficile
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Utiliser Expo env vars
|
|
const SENTRY_DSN = process.env.EXPO_PUBLIC_SENTRY_DSN || '';
|
|
|
|
// Créer .env.example
|
|
EXPO_PUBLIC_SENTRY_DSN=
|
|
EXPO_PUBLIC_API_URL=http://localhost:8080/api
|
|
EXPO_PUBLIC_WS_URL=ws://localhost:8081/ws
|
|
```
|
|
|
|
**Validation**:
|
|
- Vérifier que toutes les env vars sont documentées
|
|
- Vérifier que `.env` est dans `.gitignore`
|
|
- Test : app échoue gracieusement si env vars manquantes
|
|
|
|
**Effet de Bord**: Nécessite migration des noms de variables
|
|
|
|
---
|
|
|
|
#### 6. **P1 — Pas de Validation des Entrées Utilisateur**
|
|
|
|
**Risque**:
|
|
- Aucune validation visible dans les composants
|
|
- Injection possible si données utilisateur envoyées au backend sans validation
|
|
|
|
**Impact**:
|
|
- Injection SQL/XSS si backend vulnérable
|
|
- Corruption de données
|
|
- Erreurs applicatives
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Créer src/utils/validation.ts
|
|
import * as yup from 'yup';
|
|
|
|
export const loginSchema = yup.object({
|
|
email: yup.string().email().required(),
|
|
password: yup.string().min(8).required(),
|
|
});
|
|
|
|
// Utiliser dans les forms
|
|
const { register, handleSubmit, formState: { errors } } = useForm({
|
|
resolver: yupResolver(loginSchema),
|
|
});
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : validation côté client
|
|
- Test : rejet des entrées invalides
|
|
- Audit : vérifier validation côté backend
|
|
|
|
**Effet de Bord**: Nécessite `react-hook-form` + `yup`
|
|
|
|
---
|
|
|
|
#### 7. **P2 — Logs Potentiellement Sensibles**
|
|
|
|
**Fichier**: `src/services/monitoring.ts`
|
|
```36:46:src/services/monitoring.ts
|
|
beforeSend(event) {
|
|
// Filtrer les erreurs sensibles
|
|
if (event.exception) {
|
|
const exception = event.exception.values?.[0];
|
|
if (exception?.value?.includes('password') ||
|
|
exception?.value?.includes('token') ||
|
|
exception?.value?.includes('key')) {
|
|
return null;
|
|
}
|
|
}
|
|
return event;
|
|
},
|
|
```
|
|
|
|
**Risque**:
|
|
- Filtrage basique par mots-clés (facilement contournable)
|
|
- Pas de sanitization des données utilisateur
|
|
- URLs, emails, IDs peuvent être loggés
|
|
|
|
**Impact**:
|
|
- Fuite de données sensibles vers Sentry
|
|
- Violation RGPD
|
|
- Exposition de PII
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
import * as Sentry from '@sentry/react-native';
|
|
|
|
const sanitizeData = (data: any): any => {
|
|
const sensitiveKeys = ['password', 'token', 'key', 'secret', 'auth', 'authorization'];
|
|
if (typeof data !== 'object' || data === null) return data;
|
|
|
|
const sanitized = { ...data };
|
|
for (const key in sanitized) {
|
|
if (sensitiveKeys.some(sk => key.toLowerCase().includes(sk))) {
|
|
sanitized[key] = '[REDACTED]';
|
|
} else if (typeof sanitized[key] === 'object') {
|
|
sanitized[key] = sanitizeData(sanitized[key]);
|
|
}
|
|
}
|
|
return sanitized;
|
|
};
|
|
|
|
beforeSend(event) {
|
|
if (event.extra) {
|
|
event.extra = sanitizeData(event.extra);
|
|
}
|
|
// ... reste
|
|
},
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : vérifier que données sensibles sont redactées
|
|
- Test : vérifier que structure est préservée
|
|
- Audit manuel : inspecter events Sentry
|
|
|
|
**Effet de Bord**: Aucun
|
|
|
|
---
|
|
|
|
#### 8. **P2 — Pas de Certificate Pinning**
|
|
|
|
**Risque**:
|
|
- Pas de pinning des certificats SSL/TLS
|
|
- Vulnérable aux attaques MITM
|
|
- Interception possible des communications
|
|
|
|
**Impact**:
|
|
- Vol de tokens en transit
|
|
- Manipulation des réponses API
|
|
- Violation de confidentialité
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Installer react-native-cert-pinner
|
|
import { pinCertificate } from 'react-native-cert-pinner';
|
|
|
|
pinCertificate({
|
|
hostname: 'api.veza-platform.com',
|
|
publicKeyHashes: [
|
|
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=',
|
|
// Ajouter les hashes réels
|
|
],
|
|
});
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : vérifier que connexions non pinées sont rejetées
|
|
- Test : vérifier que connexions pinées fonctionnent
|
|
- Audit : tester avec proxy (Burp, Charles)
|
|
|
|
**Effet de Bord**: Nécessite `react-native-cert-pinner` (dépendance native)
|
|
|
|
---
|
|
|
|
#### 9. **P2 — Pas de Rate Limiting Côté Client**
|
|
|
|
**Risque**:
|
|
- Aucune limitation des tentatives de login
|
|
- Brute force possible
|
|
- DoS local possible
|
|
|
|
**Impact**:
|
|
- Compromission de comptes par brute force
|
|
- Consommation excessive de ressources
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Dans authSlice
|
|
interface AuthState {
|
|
// ...
|
|
loginAttempts: number;
|
|
lastLoginAttempt: number | null;
|
|
lockedUntil: number | null;
|
|
}
|
|
|
|
// Dans le reducer
|
|
loginFailure: (state, action) => {
|
|
state.loginAttempts += 1;
|
|
state.lastLoginAttempt = Date.now();
|
|
|
|
if (state.loginAttempts >= 5) {
|
|
state.lockedUntil = Date.now() + 15 * 60 * 1000; // 15 min
|
|
}
|
|
// ... reste
|
|
},
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : vérifier lock après 5 tentatives
|
|
- Test : vérifier déverrouillage après timeout
|
|
- Test E2E : flow de brute force
|
|
|
|
**Effet de Bord**: Aucun
|
|
|
|
---
|
|
|
|
#### 10. **P3 — Pas de Biometric Auth**
|
|
|
|
**Risque**:
|
|
- Pas d'authentification biométrique
|
|
- UX moins sécurisée
|
|
- Dépendance uniquement aux mots de passe
|
|
|
|
**Impact**:
|
|
- UX moins bonne
|
|
- Moins sécurisé que biométrie + PIN
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
import * as LocalAuthentication from 'expo-local-authentication';
|
|
|
|
const authenticateWithBiometrics = async (): Promise<boolean> => {
|
|
const hasHardware = await LocalAuthentication.hasHardwareAsync();
|
|
if (!hasHardware) return false;
|
|
|
|
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
|
|
if (!isEnrolled) return false;
|
|
|
|
const result = await LocalAuthentication.authenticateAsync({
|
|
promptMessage: 'Authentifiez-vous pour accéder à Veza',
|
|
cancelLabel: 'Annuler',
|
|
});
|
|
|
|
return result.success;
|
|
};
|
|
```
|
|
|
|
**Validation**:
|
|
- Test : vérifier support hardware
|
|
- Test : vérifier flow biométrique
|
|
- Test : fallback si biométrie indisponible
|
|
|
|
**Effet de Bord**: Nécessite `expo-local-authentication`
|
|
|
|
---
|
|
|
|
### Autres Risques Sécurité (Non Prioritaires)
|
|
|
|
- **P3** : Pas de Content Security Policy (CSP) — non applicable mobile natif
|
|
- **P3** : Pas de validation des certificats SSL — partiellement couvert par pinning
|
|
- **P3** : Logs console en production — vérifier configuration build
|
|
|
|
---
|
|
|
|
## 📊 OBSERVABILITY & PROD READINESS GAPS
|
|
|
|
### Logs Structurés
|
|
|
|
| Aspect | Status | Problèmes |
|
|
|--------|--------|-----------|
|
|
| **Format** | ⚠️ **PARTIEL** | Sentry configuré mais incompatible |
|
|
| **Niveaux** | ⚠️ **PARTIEL** | `captureMessage` avec levels mais pas de logs structurés |
|
|
| **Corrélation** | ❌ **ABSENT** | Pas de `request_id` / `trace_id` |
|
|
| **Context** | ⚠️ **PARTIEL** | `setContext` disponible mais pas utilisé systématiquement |
|
|
|
|
**Gaps**:
|
|
- Pas de logger structuré (Winston, Pino)
|
|
- Pas de corrélation entre logs
|
|
- Pas de contexte utilisateur automatique
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Créer src/utils/logger.ts
|
|
import { monitoringService } from '../services/monitoring';
|
|
|
|
interface LogContext {
|
|
userId?: string;
|
|
requestId?: string;
|
|
feature?: string;
|
|
[key: string]: any;
|
|
}
|
|
|
|
export const logger = {
|
|
info: (message: string, context?: LogContext) => {
|
|
monitoringService.setContext('log', context || {});
|
|
monitoringService.captureMessage(message, 'info');
|
|
},
|
|
error: (message: string, error?: Error, context?: LogContext) => {
|
|
monitoringService.setContext('log', context || {});
|
|
if (error) {
|
|
monitoringService.captureException(error, context);
|
|
} else {
|
|
monitoringService.captureMessage(message, 'error');
|
|
}
|
|
},
|
|
// ... autres niveaux
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### Metrics
|
|
|
|
| Métrique | Status | Problèmes |
|
|
|----------|--------|-----------|
|
|
| **Performance** | ⚠️ **PARTIEL** | `capturePerformanceMetrics` disponible mais pas utilisé |
|
|
| **Navigation** | ⚠️ **PARTIEL** | `captureNavigationMetrics` disponible mais pas utilisé |
|
|
| **API** | ⚠️ **PARTIEL** | `captureAPIMetrics` disponible mais pas utilisé |
|
|
| **Business** | ❌ **ABSENT** | Aucune métrique business |
|
|
|
|
**Gaps**:
|
|
- Métriques définies mais non instrumentées
|
|
- Pas de dashboard de métriques
|
|
- Pas d'alertes basées sur métriques
|
|
|
|
---
|
|
|
|
### Healthchecks
|
|
|
|
| Aspect | Status | Problèmes |
|
|
|--------|--------|-----------|
|
|
| **App Health** | ❌ **ABSENT** | Pas de `/healthz` ou équivalent |
|
|
| **Dependencies** | ❌ **ABSENT** | Pas de vérification DB/API/WS |
|
|
| **Readiness** | ❌ **ABSENT** | Pas de `/readyz` |
|
|
|
|
**Gaps**:
|
|
- Pas de healthcheck pour monitoring externe
|
|
- Pas de vérification de connectivité backend
|
|
- Pas de status de synchronisation offline
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Créer src/services/health.ts
|
|
export const checkHealth = async (): Promise<{
|
|
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
checks: Record<string, boolean>;
|
|
}> => {
|
|
const checks = {
|
|
api: await checkAPI(),
|
|
websocket: await checkWebSocket(),
|
|
storage: await checkStorage(),
|
|
};
|
|
|
|
const allHealthy = Object.values(checks).every(Boolean);
|
|
|
|
return {
|
|
status: allHealthy ? 'healthy' : 'degraded',
|
|
checks,
|
|
};
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
### Timeouts & Retries
|
|
|
|
| Aspect | Status | Problèmes |
|
|
|--------|--------|-----------|
|
|
| **API Timeouts** | ❌ **ABSENT** | Pas de timeout configuré |
|
|
| **Retry Logic** | ⚠️ **PARTIEL** | `retryCount` dans offline mais pas de stratégie |
|
|
| **Circuit Breaker** | ❌ **ABSENT** | Pas de circuit breaker |
|
|
|
|
**Gaps**:
|
|
- Requêtes API peuvent bloquer indéfiniment
|
|
- Pas de retry exponentiel
|
|
- Pas de circuit breaker pour éviter surcharge
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Créer src/services/api.ts avec retry
|
|
import axios, { AxiosError } from 'axios';
|
|
|
|
const apiClient = axios.create({
|
|
timeout: 10000, // 10s
|
|
});
|
|
|
|
apiClient.interceptors.response.use(
|
|
(response) => response,
|
|
async (error: AxiosError) => {
|
|
const config = error.config as any;
|
|
if (!config || !config.retry) {
|
|
config.retry = 0;
|
|
}
|
|
|
|
if (config.retry < 3 && error.response?.status >= 500) {
|
|
config.retry += 1;
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * config.retry));
|
|
return apiClient(config);
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
### Gestion de Charge
|
|
|
|
| Aspect | Status | Problèmes |
|
|
|--------|--------|-----------|
|
|
| **Pool DB** | N/A | Client uniquement |
|
|
| **Limits WS** | ❌ **ABSENT** | Pas de limite de connexions |
|
|
| **Streaming** | ❌ **ABSENT** | Pas de gestion de buffering |
|
|
|
|
**Gaps**:
|
|
- Pas de limite de requêtes simultanées
|
|
- Pas de queue pour actions offline
|
|
- Pas de priorisation des requêtes
|
|
|
|
---
|
|
|
|
### Migrations & Compatibilité
|
|
|
|
| Aspect | Status | Problèmes |
|
|
|--------|--------|-----------|
|
|
| **Schema Redux** | ❌ **ABSENT** | Pas de migration de state |
|
|
| **Versioning** | ❌ **ABSENT** | Pas de version d'app dans state |
|
|
| **Compatibilité** | ❌ **ABSENT** | Pas de gestion de breaking changes |
|
|
|
|
**Gaps**:
|
|
- Changements de schema Redux peuvent casser l'app
|
|
- Pas de migration automatique du state persisté
|
|
- Pas de versioning des données
|
|
|
|
**Fix Minimal**:
|
|
```typescript
|
|
// Dans store.ts
|
|
import { createMigrate } from 'redux-persist';
|
|
|
|
const migrations = {
|
|
0: (state: any) => {
|
|
// Migration v0 -> v1
|
|
return {
|
|
...state,
|
|
auth: {
|
|
...state.auth,
|
|
// Ajouter nouveaux champs avec defaults
|
|
},
|
|
};
|
|
},
|
|
1: (state: any) => {
|
|
// Migration v1 -> v2
|
|
return state;
|
|
},
|
|
};
|
|
|
|
const persistConfig = {
|
|
// ...
|
|
version: 2,
|
|
migrate: createMigrate(migrations, { debug: true }),
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## ⚡ PERFORMANCE NOTES
|
|
|
|
### Hotspots Évidents
|
|
|
|
#### 1. **Re-renders Inutiles**
|
|
|
|
**Fichier**: `src/screens/DashboardScreen.tsx`
|
|
```18:19:src/screens/DashboardScreen.tsx
|
|
const { features, loading: featuresLoading } = useSelector((state: RootState) => state.features);
|
|
const { realtimeData, loading: analyticsLoading } = useSelector((state: RootState) => state.analytics);
|
|
```
|
|
|
|
**Problème**: Sélecteurs non mémorisés, re-render à chaque changement de state.
|
|
|
|
**Fix**:
|
|
```typescript
|
|
import { createSelector } from '@reduxjs/toolkit';
|
|
import { useMemo } from 'react';
|
|
|
|
const selectFeatures = (state: RootState) => state.features.features;
|
|
const selectFeaturesLoading = (state: RootState) => state.features.loading;
|
|
|
|
const DashboardScreen = () => {
|
|
const features = useSelector(selectFeatures);
|
|
const featuresLoading = useSelector(selectFeaturesLoading);
|
|
// ...
|
|
};
|
|
```
|
|
|
|
**Impact**: Réduction ~30% des re-renders.
|
|
|
|
---
|
|
|
|
#### 2. **Pas de Memoization des Composants**
|
|
|
|
**Fichier**: `src/screens/DashboardScreen.tsx`
|
|
```109:143:src/screens/DashboardScreen.tsx
|
|
{features.slice(0, 5).map((feature) => (
|
|
<TouchableOpacity key={feature.id} style={styles.featureCard}>
|
|
{/* ... */}
|
|
</TouchableOpacity>
|
|
))}
|
|
```
|
|
|
|
**Problème**: Re-création de composants à chaque render.
|
|
|
|
**Fix**:
|
|
```typescript
|
|
const FeatureCard = React.memo(({ feature }: { feature: Feature }) => {
|
|
// ...
|
|
});
|
|
|
|
// Dans le render
|
|
{features.slice(0, 5).map((feature) => (
|
|
<FeatureCard key={feature.id} feature={feature} />
|
|
))}
|
|
```
|
|
|
|
**Impact**: Réduction ~20% du temps de render.
|
|
|
|
---
|
|
|
|
#### 3. **Calculs dans le Render**
|
|
|
|
**Fichier**: `src/screens/DashboardScreen.tsx`
|
|
```40:64:src/screens/DashboardScreen.tsx
|
|
const getStatusColor = (status: string) => {
|
|
switch (status) {
|
|
// ...
|
|
}
|
|
};
|
|
|
|
const getStatusIcon = (status: string) => {
|
|
switch (status) {
|
|
// ...
|
|
}
|
|
};
|
|
```
|
|
|
|
**Problème**: Fonctions recréées à chaque render.
|
|
|
|
**Fix**:
|
|
```typescript
|
|
const STATUS_CONFIG = {
|
|
running: { color: '#10B981', icon: 'checkmark-circle' },
|
|
error: { color: '#EF4444', icon: 'close-circle' },
|
|
stopped: { color: '#6B7280', icon: 'stop-circle' },
|
|
} as const;
|
|
|
|
// Utiliser directement
|
|
const config = STATUS_CONFIG[status] || { color: '#F59E0B', icon: 'help-circle' };
|
|
```
|
|
|
|
**Impact**: Réduction ~10% du temps de render.
|
|
|
|
---
|
|
|
|
#### 4. **Pas de Lazy Loading**
|
|
|
|
**Fichier**: `App.tsx`
|
|
```11:18:App.tsx
|
|
import DashboardScreen from './src/screens/DashboardScreen';
|
|
import FeaturesScreen from './src/screens/FeaturesScreen';
|
|
import AnalyticsScreen from './src/screens/AnalyticsScreen';
|
|
import MediaScreen from './src/screens/MediaScreen';
|
|
import ChatScreen from './src/screens/ChatScreen';
|
|
import SettingsScreen from './src/screens/SettingsScreen';
|
|
import LoginScreen from './src/screens/auth/LoginScreen';
|
|
import RegisterScreen from './src/screens/auth/RegisterScreen';
|
|
```
|
|
|
|
**Problème**: Tous les screens chargés au démarrage.
|
|
|
|
**Fix**:
|
|
```typescript
|
|
const DashboardScreen = React.lazy(() => import('./src/screens/DashboardScreen'));
|
|
const FeaturesScreen = React.lazy(() => import('./src/screens/FeaturesScreen'));
|
|
// ... etc
|
|
|
|
// Dans le navigator
|
|
<Suspense fallback={<LoadingScreen />}>
|
|
<Tab.Screen name="Dashboard" component={DashboardScreen} />
|
|
</Suspense>
|
|
```
|
|
|
|
**Impact**: Réduction ~40% du temps de démarrage initial.
|
|
|
|
---
|
|
|
|
#### 5. **Pas de Debounce/Throttle**
|
|
|
|
**Fichier**: `src/screens/DashboardScreen.tsx`
|
|
```31:38:src/screens/DashboardScreen.tsx
|
|
const onRefresh = async () => {
|
|
setRefreshing(true);
|
|
await Promise.all([
|
|
dispatch(fetchFeatures()),
|
|
dispatch(fetchAnalytics()),
|
|
]);
|
|
setRefreshing(false);
|
|
};
|
|
```
|
|
|
|
**Problème**: Pas de protection contre les refresh multiples.
|
|
|
|
**Fix**:
|
|
```typescript
|
|
import { useCallback } from 'react';
|
|
import { debounce } from 'lodash';
|
|
|
|
const onRefresh = useCallback(
|
|
debounce(async () => {
|
|
setRefreshing(true);
|
|
await Promise.all([
|
|
dispatch(fetchFeatures()),
|
|
dispatch(fetchAnalytics()),
|
|
]);
|
|
setRefreshing(false);
|
|
}, 1000),
|
|
[dispatch]
|
|
);
|
|
```
|
|
|
|
**Impact**: Évite les requêtes multiples inutiles.
|
|
|
|
---
|
|
|
|
### Optimisations Mesurables
|
|
|
|
| Optimisation | Impact Estimé | Effort | Priorité |
|
|
|--------------|---------------|--------|-----------|
|
|
| Memoization sélecteurs | -30% re-renders | S | P1 |
|
|
| Memoization composants | -20% render time | S | P1 |
|
|
| Lazy loading screens | -40% startup time | M | P1 |
|
|
| Debounce refresh | -50% requêtes inutiles | S | P2 |
|
|
| Virtualisation listes | -60% mémoire (grandes listes) | M | P2 |
|
|
| Image optimization | -30% bundle size | M | P2 |
|
|
| Code splitting | -25% bundle initial | L | P2 |
|
|
|
|
---
|
|
|
|
## 🐛 ISSUE LIST PRIORISÉE (P0→P3)
|
|
|
|
### P0 — CRITIQUE (Build Breakers, Sécurité Exploitable)
|
|
|
|
#### MOD-P0-001: Fichiers de Configuration Absents
|
|
- **Titre**: `package.json`, `tsconfig.json`, `app.json` manquants
|
|
- **Impact**: **BUILD IMPOSSIBLE** — Le module ne peut pas être compilé, testé, ou exécuté
|
|
- **Preuve**:
|
|
- Aucun `package.json` trouvé
|
|
- Aucun `tsconfig.json` trouvé
|
|
- Aucun `app.json` (Expo) trouvé
|
|
- **Cause Racine**: Module créé sans configuration de base
|
|
- **Fix Minimal**:
|
|
```json
|
|
// package.json
|
|
{
|
|
"name": "veza-mobile",
|
|
"version": "1.0.0",
|
|
"main": "node_modules/expo/AppEntry.js",
|
|
"scripts": {
|
|
"start": "expo start",
|
|
"android": "expo start --android",
|
|
"ios": "expo start --ios",
|
|
"web": "expo start --web",
|
|
"test": "jest",
|
|
"lint": "eslint . --ext .ts,.tsx"
|
|
},
|
|
"dependencies": {
|
|
"expo": "~49.0.0",
|
|
"react": "18.2.0",
|
|
"react-native": "0.72.0",
|
|
"@react-navigation/native": "^6.1.0",
|
|
"@react-navigation/bottom-tabs": "^6.5.0",
|
|
"@react-navigation/stack": "^6.3.0",
|
|
"@reduxjs/toolkit": "^1.9.0",
|
|
"react-redux": "^8.1.0",
|
|
"redux-persist": "^6.0.0",
|
|
"@react-native-async-storage/async-storage": "^1.19.0",
|
|
"@expo/vector-icons": "^13.0.0",
|
|
"expo-status-bar": "~1.6.0",
|
|
"@sentry/react-native": "^5.0.0"
|
|
},
|
|
"devDependencies": {
|
|
"@types/react": "~18.2.0",
|
|
"@types/react-native": "~0.72.0",
|
|
"typescript": "^5.1.0",
|
|
"@testing-library/react-native": "^12.0.0",
|
|
"@testing-library/jest-native": "^5.4.0",
|
|
"jest": "^29.0.0",
|
|
"jest-expo": "~49.0.0"
|
|
}
|
|
}
|
|
```
|
|
- **Plan de Validation**:
|
|
- `npm install` → succès
|
|
- `npm run start` → Expo démarre
|
|
- `npm test` → tests exécutables
|
|
- **Effet de Bord**: Nécessite définition des versions exactes
|
|
- **Effort**: M (2-3h)
|
|
|
|
---
|
|
|
|
#### MOD-P0-002: 7 Screens Importés Mais Absents
|
|
- **Titre**: Imports de screens inexistants dans `App.tsx`
|
|
- **Impact**: **CRASH AU DÉMARRAGE** — L'app ne peut pas démarrer
|
|
- **Preuve**:
|
|
```12:18:App.tsx
|
|
import FeaturesScreen from './src/screens/FeaturesScreen';
|
|
import AnalyticsScreen from './src/screens/AnalyticsScreen';
|
|
import MediaScreen from './src/screens/MediaScreen';
|
|
import ChatScreen from './src/screens/ChatScreen';
|
|
import SettingsScreen from './src/screens/SettingsScreen';
|
|
import LoginScreen from './src/screens/auth/LoginScreen';
|
|
import RegisterScreen from './src/screens/auth/RegisterScreen';
|
|
```
|
|
- **Cause Racine**: Screens référencés mais jamais créés
|
|
- **Fix Minimal**: Créer les 7 screens avec structure minimale
|
|
- **Plan de Validation**:
|
|
- `npm run start` → app démarre sans erreur
|
|
- Navigation entre screens fonctionne
|
|
- **Effet de Bord**: Screens vides à implémenter
|
|
- **Effort**: M (3-4h pour les 7 screens)
|
|
|
|
---
|
|
|
|
#### MOD-P0-003: 5 Redux Slices Importés Mais Absents
|
|
- **Titre**: Reducers manquants dans `store.ts`
|
|
- **Impact**: **CRASH AU DÉMARRAGE** — Store Redux invalide
|
|
- **Preuve**:
|
|
```8:12:src/store/store.ts
|
|
import featuresReducer from './slices/featuresSlice';
|
|
import analyticsReducer from './slices/analyticsSlice';
|
|
import mediaReducer from './slices/mediaSlice';
|
|
import chatReducer from './slices/chatSlice';
|
|
import uiReducer from './slices/uiSlice';
|
|
```
|
|
- **Cause Racine**: Slices référencés mais jamais créés
|
|
- **Fix Minimal**: Créer les 5 slices avec state initial
|
|
- **Plan de Validation**:
|
|
- Store se configure sans erreur
|
|
- Reducers fonctionnent
|
|
- **Effet de Bord**: State initial à définir
|
|
- **Effort**: M (2-3h)
|
|
|
|
---
|
|
|
|
#### MOD-P0-004: Composants UI Incompatibles React Native
|
|
- **Titre**: `Button`, `Card`, `Modal` utilisent du code web
|
|
- **Impact**: **CRASH AU RENDU** — Composants ne fonctionnent pas en React Native
|
|
- **Preuve**:
|
|
- `Button.tsx` : utilise `<button>` et classes Tailwind
|
|
- `Card.tsx` : utilise `<div>` et classes Tailwind
|
|
- `Modal.tsx` : utilise `react-dom` `createPortal`
|
|
- **Cause Racine**: Composants copiés depuis une app web
|
|
- **Fix Minimal**: Réécrire avec composants React Native (`TouchableOpacity`, `View`, `Text`, `Modal` de RN)
|
|
- **Plan de Validation**:
|
|
- Composants rendent correctement
|
|
- Styles appliqués (StyleSheet au lieu de Tailwind)
|
|
- Modal fonctionne (Modal RN au lieu de Portal)
|
|
- **Effet de Bord**: Styles à réécrire
|
|
- **Effort**: M (3-4h)
|
|
|
|
---
|
|
|
|
#### MOD-P0-005: Sentry Incompatible (React Web au lieu de React Native)
|
|
- **Titre**: `@sentry/react` utilisé au lieu de `@sentry/react-native`
|
|
- **Impact**: **CRASH À L'INITIALISATION** — Sentry ne peut pas s'initialiser
|
|
- **Preuve**:
|
|
```1:2:src/services/monitoring.ts
|
|
import * as Sentry from '@sentry/react';
|
|
import { BrowserTracing } from '@sentry/tracing';
|
|
```
|
|
- **Cause Racine**: Code copié depuis une app web
|
|
- **Fix Minimal**:
|
|
```typescript
|
|
import * as Sentry from '@sentry/react-native';
|
|
|
|
Sentry.init({
|
|
dsn: process.env.EXPO_PUBLIC_SENTRY_DSN,
|
|
enableInExpoDevelopment: false,
|
|
debug: __DEV__,
|
|
});
|
|
```
|
|
- **Plan de Validation**:
|
|
- Sentry s'initialise sans erreur
|
|
- Erreurs capturées correctement
|
|
- **Effet de Bord**: Nécessite `@sentry/react-native`
|
|
- **Effort**: S (30min)
|
|
|
|
---
|
|
|
|
#### MOD-P0-006: Authentification Hardcodée à `false`
|
|
- **Titre**: `isAuthenticated` toujours `false` dans `App.tsx`
|
|
- **Impact**: **BYPASS SÉCURITÉ** — Utilisateur ne peut jamais s'authentifier, toutes routes accessibles
|
|
- **Preuve**:
|
|
```131:132:App.tsx
|
|
// TODO: Check authentication state
|
|
const isAuthenticated = false; // This should come from auth state
|
|
```
|
|
- **Cause Racine**: TODO non implémenté
|
|
- **Fix Minimal**: Utiliser `useSelector` pour lire `state.auth.isAuthenticated`
|
|
- **Plan de Validation**:
|
|
- Utilisateur non auth → AuthNavigator
|
|
- Utilisateur auth → MainTabNavigator
|
|
- **Effet de Bord**: Aucun
|
|
- **Effort**: S (15min)
|
|
|
|
---
|
|
|
|
#### MOD-P0-007: Tokens JWT Stockés en Clair
|
|
- **Titre**: Tokens stockés dans AsyncStorage sans chiffrement
|
|
- **Impact**: **FUITE DE DONNÉES** — Tokens lisibles si device compromis
|
|
- **Preuve**:
|
|
```16:20:src/store/store.ts
|
|
const persistConfig = {
|
|
key: 'root',
|
|
storage: AsyncStorage,
|
|
whitelist: ['auth', 'offline'],
|
|
};
|
|
```
|
|
- **Cause Racine**: Pas de chiffrement configuré
|
|
- **Fix Minimal**: Utiliser `react-native-encrypted-storage`
|
|
- **Plan de Validation**:
|
|
- Tokens chiffrés dans le storage
|
|
- Décryptage fonctionne
|
|
- **Effet de Bord**: Nécessite dépendance native
|
|
- **Effort**: M (1-2h)
|
|
|
|
---
|
|
|
|
#### MOD-P0-008: Pas de Validation JWT
|
|
- **Titre**: Aucune validation d'expiration/signature des tokens
|
|
- **Impact**: **SÉCURITÉ** — Tokens expirés utilisables indéfiniment
|
|
- **Preuve**: `authSlice.ts` stocke tokens sans validation
|
|
- **Cause Racine**: Validation non implémentée
|
|
- **Fix Minimal**: Ajouter validation avec `jwt-decode`
|
|
- **Plan de Validation**:
|
|
- Token expiré rejeté
|
|
- Token invalide rejeté
|
|
- **Effet de Bord**: Nécessite `jwt-decode`
|
|
- **Effort**: S (30min)
|
|
|
|
---
|
|
|
|
### P1 — HAUTE PRIORITÉ (Bugs Fréquents, Dette Bloquante)
|
|
|
|
#### MOD-P1-001: MSW Server Mock Absent
|
|
- **Titre**: `./mocks/server` importé mais absent
|
|
- **Impact**: **TESTS CASSÉS** — Tests ne peuvent pas démarrer
|
|
- **Preuve**:
|
|
```3:3:src/setupTests.ts
|
|
import { server } from './mocks/server';
|
|
```
|
|
- **Cause Racine**: Mock server non créé
|
|
- **Fix Minimal**: Créer `src/mocks/server.ts` avec MSW
|
|
- **Plan de Validation**:
|
|
- Tests démarrent sans erreur
|
|
- Mocks fonctionnent
|
|
- **Effet de Bord**: Aucun
|
|
- **Effort**: S (1h)
|
|
|
|
---
|
|
|
|
#### MOD-P1-002: Test Import Incorrect (Dashboard vs DashboardScreen)
|
|
- **Titre**: Test importe `Dashboard` mais fichier est `DashboardScreen`
|
|
- **Impact**: **TEST CASSÉ** — Import échoue
|
|
- **Preuve**:
|
|
```5:5:src/pages/Dashboard/__tests__/Dashboard.test.tsx
|
|
import Dashboard from '../Dashboard';
|
|
```
|
|
Mais le fichier est `src/screens/DashboardScreen.tsx`
|
|
- **Cause Racine**: Incohérence de nommage
|
|
- **Fix Minimal**: Corriger l'import ou renommer le composant
|
|
- **Plan de Validation**:
|
|
- Test s'exécute
|
|
- Composant rendu correctement
|
|
- **Effet de Bord**: Aucun
|
|
- **Effort**: S (15min)
|
|
|
|
---
|
|
|
|
#### MOD-P1-003: Pas de Variables d'Environnement
|
|
- **Titre**: Pas de `.env` ni `.env.example`
|
|
- **Impact**: **CONFIGURATION** — Variables hardcodées ou manquantes
|
|
- **Preuve**: `monitoring.ts` utilise `process.env.REACT_APP_SENTRY_DSN` (mauvais préfixe pour Expo)
|
|
- **Cause Racine**: Configuration non documentée
|
|
- **Fix Minimal**: Créer `.env.example` et utiliser `EXPO_PUBLIC_*`
|
|
- **Plan de Validation**:
|
|
- Variables documentées
|
|
- `.env` dans `.gitignore`
|
|
- **Effet de Bord**: Migration des noms de variables
|
|
- **Effort**: S (30min)
|
|
|
|
---
|
|
|
|
#### MOD-P1-004: Pas de Error Boundary
|
|
- **Titre**: Aucun Error Boundary React
|
|
- **Impact**: **UX** — Crashes non gérés, écran blanc
|
|
- **Preuve**: Aucun `ErrorBoundary` dans le code
|
|
- **Cause Racine**: Non implémenté
|
|
- **Fix Minimal**: Créer `ErrorBoundary` et wrapper l'app
|
|
- **Plan de Validation**:
|
|
- Erreur non gérée → écran d'erreur
|
|
- Logging de l'erreur
|
|
- **Effet de Bord**: Aucun
|
|
- **Effort**: S (1h)
|
|
|
|
---
|
|
|
|
#### MOD-P1-005: Pas de Timeout API
|
|
- **Titre**: Requêtes API sans timeout
|
|
- **Impact**: **UX** — App peut bloquer indéfiniment
|
|
- **Preuve**: Pas de service API configuré avec timeout
|
|
- **Cause Racine**: Service API non créé
|
|
- **Fix Minimal**: Créer service API avec axios et timeout
|
|
- **Plan de Validation**:
|
|
- Timeout après 10s
|
|
- Erreur gérée gracieusement
|
|
- **Effet de Bord**: Nécessite service API
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P1-006: Pas de Retry Logic
|
|
- **Titre**: Pas de retry pour requêtes échouées
|
|
- **Impact**: **ROBUSTESSE** — Échecs temporaires non récupérés
|
|
- **Preuve**: `offlineSlice` a `retryCount` mais pas de logique de retry
|
|
- **Cause Racine**: Logique non implémentée
|
|
- **Fix Minimal**: Implémenter retry exponentiel dans service API
|
|
- **Plan de Validation**:
|
|
- Retry après échec 5xx
|
|
- Max 3 tentatives
|
|
- **Effet de Bord**: Aucun
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P1-007: Re-renders Inutiles (Sélecteurs Non Mémorisés)
|
|
- **Titre**: Sélecteurs Redux recréés à chaque render
|
|
- **Impact**: **PERFORMANCE** — Re-renders inutiles
|
|
- **Preuve**: `DashboardScreen.tsx` utilise `useSelector` avec sélecteurs inline
|
|
- **Cause Racine**: Pas de mémorisation
|
|
- **Fix Minimal**: Utiliser `createSelector` ou mémoriser les sélecteurs
|
|
- **Plan de Validation**:
|
|
- Réduction des re-renders mesurée
|
|
- Performance améliorée
|
|
- **Effet de Bord**: Aucun
|
|
- **Effort**: S (1h)
|
|
|
|
---
|
|
|
|
#### MOD-P1-008: Pas de Lazy Loading Screens
|
|
- **Titre**: Tous les screens chargés au démarrage
|
|
- **Impact**: **PERFORMANCE** — Startup lent
|
|
- **Preuve**: Tous les screens importés statiquement dans `App.tsx`
|
|
- **Cause Racine**: Pas de code splitting
|
|
- **Fix Minimal**: Utiliser `React.lazy` et `Suspense`
|
|
- **Plan de Validation**:
|
|
- Startup plus rapide
|
|
- Screens chargés à la demande
|
|
- **Effet de Bord**: Nécessite fallback UI
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
### P2 — MOYENNE PRIORITÉ (Qualité, Maintenabilité)
|
|
|
|
#### MOD-P2-001: Pas de Validation des Entrées
|
|
- **Titre**: Aucune validation des formulaires
|
|
- **Impact**: **SÉCURITÉ** — Données invalides envoyées au backend
|
|
- **Preuve**: Pas de validation visible dans les composants
|
|
- **Fix Minimal**: Ajouter `react-hook-form` + `yup`
|
|
- **Effort**: M (2-3h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-002: Pas de Rate Limiting Côté Client
|
|
- **Titre**: Pas de limitation des tentatives de login
|
|
- **Impact**: **SÉCURITÉ** — Brute force possible
|
|
- **Preuve**: `authSlice` n'a pas de compteur de tentatives
|
|
- **Fix Minimal**: Ajouter `loginAttempts` et lock après 5 tentatives
|
|
- **Effort**: S (1h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-003: Logs Potentiellement Sensibles
|
|
- **Titre**: Filtrage Sentry basique par mots-clés
|
|
- **Impact**: **SÉCURITÉ** — Fuite de données possibles
|
|
- **Preuve**: `beforeSend` filtre seulement quelques mots-clés
|
|
- **Fix Minimal**: Améliorer sanitization récursive
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-004: Pas de Certificate Pinning
|
|
- **Titre**: Pas de pinning SSL/TLS
|
|
- **Impact**: **SÉCURITÉ** — Vulnérable MITM
|
|
- **Preuve**: Pas de configuration de pinning
|
|
- **Fix Minimal**: Ajouter `react-native-cert-pinner`
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-005: Pas de Healthcheck
|
|
- **Titre**: Pas de healthcheck pour monitoring
|
|
- **Impact**: **OBSERVABILITÉ** — Impossible de monitorer l'état de l'app
|
|
- **Preuve**: Pas de service health
|
|
- **Fix Minimal**: Créer service health avec checks API/WS/storage
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-006: Pas de Migrations Redux Persist
|
|
- **Titre**: Pas de migration de schema Redux
|
|
- **Impact**: **COMPATIBILITÉ** — Breaking changes cassent l'app
|
|
- **Preuve**: `persistConfig` n'a pas de `migrate`
|
|
- **Fix Minimal**: Ajouter migrations avec `createMigrate`
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-007: Pas de Memoization Composants
|
|
- **Titre**: Composants recréés à chaque render
|
|
- **Impact**: **PERFORMANCE** — Renders inutiles
|
|
- **Preuve**: `DashboardScreen` map sans `React.memo`
|
|
- **Fix Minimal**: Wrapper composants avec `React.memo`
|
|
- **Effort**: S (1h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-008: Pas de Debounce Refresh
|
|
- **Titre**: Refresh peut être déclenché plusieurs fois rapidement
|
|
- **Impact**: **PERFORMANCE** — Requêtes multiples inutiles
|
|
- **Preuve**: `onRefresh` pas debounced
|
|
- **Fix Minimal**: Ajouter debounce 1s
|
|
- **Effort**: S (30min)
|
|
|
|
---
|
|
|
|
#### MOD-P2-009: Pas de Virtualisation Listes
|
|
- **Titre**: Pas de virtualisation pour longues listes
|
|
- **Impact**: **PERFORMANCE** — Ralentissement avec beaucoup d'items
|
|
- **Preuve**: `features.map` sans virtualisation
|
|
- **Fix Minimal**: Utiliser `FlatList` avec `getItemLayout`
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P2-010: Pas de Corrélation Logs (request_id)
|
|
- **Titre**: Pas de `request_id` / `trace_id` dans les logs
|
|
- **Impact**: **OBSERVABILITÉ** — Debugging difficile
|
|
- **Preuve**: Pas de corrélation dans `monitoring.ts`
|
|
- **Fix Minimal**: Ajouter `request_id` dans contexte Sentry
|
|
- **Effort**: S (1h)
|
|
|
|
---
|
|
|
|
### P3 — BASSE PRIORITÉ (Cosmétique, Refactors)
|
|
|
|
#### MOD-P3-001: Pas de Biometric Auth
|
|
- **Titre**: Pas d'authentification biométrique
|
|
- **Impact**: **UX** — Moins sécurisé et moins pratique
|
|
- **Fix Minimal**: Ajouter `expo-local-authentication`
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P3-002: Structure Mélangée (pages/ vs screens/)
|
|
- **Titre**: Mélange de `pages/` et `screens/`
|
|
- **Impact**: **MAINTENABILITÉ** — Confusion
|
|
- **Preuve**: `src/pages/Dashboard/` et `src/screens/DashboardScreen.tsx`
|
|
- **Fix Minimal**: Standardiser sur `screens/`
|
|
- **Effort**: S (30min)
|
|
|
|
---
|
|
|
|
#### MOD-P3-003: Pas de Documentation
|
|
- **Titre**: Pas de README ni de documentation
|
|
- **Impact**: **DX** — Onboarding difficile
|
|
- **Preuve**: Aucun fichier `.md`
|
|
- **Fix Minimal**: Créer README avec setup, architecture, API
|
|
- **Effort**: M (2h)
|
|
|
|
---
|
|
|
|
#### MOD-P3-004: Pas de Prettier/ESLint Config
|
|
- **Titre**: Pas de configuration de formatage
|
|
- **Impact**: **QUALITÉ** — Code incohérent
|
|
- **Preuve**: Pas de `.prettierrc` ni `.eslintrc`
|
|
- **Fix Minimal**: Ajouter config Prettier + ESLint
|
|
- **Effort**: S (30min)
|
|
|
|
---
|
|
|
|
#### MOD-P3-005: Hardcoded Values (Stats Dashboard)
|
|
- **Titre**: Valeurs hardcodées dans `DashboardScreen`
|
|
- **Impact**: **MAINTENABILITÉ** — Données incorrectes
|
|
- **Preuve**:
|
|
```95:102:src/screens/DashboardScreen.tsx
|
|
<Text style={styles.statNumber}>12</Text>
|
|
<Text style={styles.statLabel}>Médias</Text>
|
|
// ...
|
|
<Text style={styles.statNumber}>5</Text>
|
|
<Text style={styles.statLabel}>Chats Actifs</Text>
|
|
```
|
|
- **Fix Minimal**: Utiliser données du state
|
|
- **Effort**: S (15min)
|
|
|
|
---
|
|
|
|
## 🚀 EXECUTION PLAN
|
|
|
|
### Checklist P0 (Ordre Strict)
|
|
|
|
1. ✅ **MOD-P0-001**: Créer `package.json` avec toutes les dépendances
|
|
2. ✅ **MOD-P0-002**: Créer `tsconfig.json` pour TypeScript
|
|
3. ✅ **MOD-P0-003**: Créer `app.json` pour Expo
|
|
4. ✅ **MOD-P0-004**: Créer les 7 screens manquants (structure minimale)
|
|
5. ✅ **MOD-P0-005**: Créer les 5 Redux slices manquants
|
|
6. ✅ **MOD-P0-006**: Réécrire composants UI (Button, Card, Modal) en React Native
|
|
7. ✅ **MOD-P0-007**: Remplacer `@sentry/react` par `@sentry/react-native`
|
|
8. ✅ **MOD-P0-008**: Fix authentification hardcodée (`isAuthenticated`)
|
|
9. ✅ **MOD-P0-009**: Implémenter chiffrement tokens (EncryptedStorage)
|
|
10. ✅ **MOD-P0-010**: Ajouter validation JWT
|
|
|
|
**Validation P0**:
|
|
```bash
|
|
npm install
|
|
npm run start # Expo démarre sans erreur
|
|
npm test # Tests exécutables
|
|
```
|
|
|
|
---
|
|
|
|
### Checklist P1 (Par Lots Cohérents)
|
|
|
|
#### Lot 1: Tests & Configuration
|
|
1. ✅ **MOD-P1-001**: Créer `src/mocks/server.ts` (MSW)
|
|
2. ✅ **MOD-P1-002**: Fix import test Dashboard
|
|
3. ✅ **MOD-P1-003**: Créer `.env.example` et fix variables
|
|
|
|
#### Lot 2: Robustesse
|
|
4. ✅ **MOD-P1-004**: Créer ErrorBoundary
|
|
5. ✅ **MOD-P1-005**: Créer service API avec timeout
|
|
6. ✅ **MOD-P1-006**: Implémenter retry logic
|
|
|
|
#### Lot 3: Performance
|
|
7. ✅ **MOD-P1-007**: Mémoriser sélecteurs Redux
|
|
8. ✅ **MOD-P1-008**: Lazy loading screens
|
|
|
|
**Validation P1**:
|
|
```bash
|
|
npm test # Tous les tests passent
|
|
npm run lint # Pas d'erreurs
|
|
# Tester ErrorBoundary manuellement
|
|
# Vérifier retry avec réseau lent
|
|
```
|
|
|
|
---
|
|
|
|
### Quick Wins (≤ 1h chacun)
|
|
|
|
1. **MOD-P0-008**: Fix `isAuthenticated` (15min)
|
|
2. **MOD-P1-002**: Fix import test (15min)
|
|
3. **MOD-P2-008**: Debounce refresh (30min)
|
|
4. **MOD-P3-005**: Fix hardcoded values (15min)
|
|
5. **MOD-P3-002**: Standardiser structure (30min)
|
|
6. **MOD-P3-004**: Ajouter Prettier/ESLint (30min)
|
|
|
|
---
|
|
|
|
### Tests à Ajouter en Priorité
|
|
|
|
#### Tests Unitaires
|
|
1. **Auth Slice**:
|
|
- `loginSuccess` valide token
|
|
- `loginFailure` incrémente attempts
|
|
- `logout` nettoie state
|
|
2. **Offline Slice**:
|
|
- `addPendingAction` ajoute action
|
|
- `removePendingAction` retire action
|
|
- `updatePendingAction` met à jour retryCount
|
|
3. **Monitoring Service**:
|
|
- `initializeSentry` ne double pas l'init
|
|
- `captureException` filtre données sensibles
|
|
- `sanitizeData` redacte correctement
|
|
|
|
#### Tests d'Intégration
|
|
1. **Auth Flow**:
|
|
- Login → token stocké → navigation Main
|
|
- Logout → token supprimé → navigation Auth
|
|
- Token expiré → refresh automatique
|
|
2. **Offline Sync**:
|
|
- Action ajoutée offline → sync quand online
|
|
- Retry après échec
|
|
- Clear après succès
|
|
|
|
#### Tests E2E
|
|
1. **Navigation**:
|
|
- Auth → Main → Screens
|
|
- Logout → retour Auth
|
|
2. **Dashboard**:
|
|
- Load data → affichage
|
|
- Refresh → reload
|
|
- Empty state → message
|
|
|
|
---
|
|
|
|
### PR Plan (Petites PRs)
|
|
|
|
#### PR #1: Configuration de Base
|
|
**Titre**: `feat: Add base configuration (package.json, tsconfig, app.json)`
|
|
**Fichiers**:
|
|
- `package.json` (nouveau)
|
|
- `tsconfig.json` (nouveau)
|
|
- `app.json` (nouveau)
|
|
- `.gitignore` (nouveau)
|
|
**Tests**: `npm install` → succès
|
|
|
|
---
|
|
|
|
#### PR #2: Fix Imports Manquants
|
|
**Titre**: `fix: Create missing screens and redux slices`
|
|
**Fichiers**:
|
|
- `src/screens/FeaturesScreen.tsx` (nouveau)
|
|
- `src/screens/AnalyticsScreen.tsx` (nouveau)
|
|
- `src/screens/MediaScreen.tsx` (nouveau)
|
|
- `src/screens/ChatScreen.tsx` (nouveau)
|
|
- `src/screens/SettingsScreen.tsx` (nouveau)
|
|
- `src/screens/auth/LoginScreen.tsx` (nouveau)
|
|
- `src/screens/auth/RegisterScreen.tsx` (nouveau)
|
|
- `src/store/slices/featuresSlice.ts` (nouveau)
|
|
- `src/store/slices/analyticsSlice.ts` (nouveau)
|
|
- `src/store/slices/mediaSlice.ts` (nouveau)
|
|
- `src/store/slices/chatSlice.ts` (nouveau)
|
|
- `src/store/slices/uiSlice.ts` (nouveau)
|
|
**Tests**: App démarre sans erreur
|
|
|
|
---
|
|
|
|
#### PR #3: Fix Composants UI React Native
|
|
**Titre**: `fix: Rewrite UI components for React Native`
|
|
**Fichiers**:
|
|
- `src/components/UI/Button.tsx` (réécrit)
|
|
- `src/components/UI/Card.tsx` (réécrit)
|
|
- `src/components/UI/Modal.tsx` (réécrit)
|
|
**Tests**: Composants rendent correctement
|
|
|
|
---
|
|
|
|
#### PR #4: Fix Sentry & Monitoring
|
|
**Titre**: `fix: Replace @sentry/react with @sentry/react-native`
|
|
**Fichiers**:
|
|
- `src/services/monitoring.ts` (modifié)
|
|
- `package.json` (dépendance mise à jour)
|
|
**Tests**: Sentry s'initialise
|
|
|
|
---
|
|
|
|
#### PR #5: Fix Authentification
|
|
**Titre**: `fix: Connect authentication state to navigation`
|
|
**Fichiers**:
|
|
- `App.tsx` (modifié)
|
|
**Tests**: Navigation fonctionne selon auth state
|
|
|
|
---
|
|
|
|
#### PR #6: Sécurité Tokens
|
|
**Titre**: `feat: Encrypt JWT tokens in storage`
|
|
**Fichiers**:
|
|
- `src/store/store.ts` (modifié)
|
|
- `package.json` (ajout `react-native-encrypted-storage`)
|
|
**Tests**: Tokens chiffrés dans storage
|
|
|
|
---
|
|
|
|
#### PR #7: Validation JWT
|
|
**Titre**: `feat: Add JWT validation (expiration, signature)`
|
|
**Fichiers**:
|
|
- `src/store/slices/authSlice.ts` (modifié)
|
|
- `src/utils/jwt.ts` (nouveau)
|
|
- `package.json` (ajout `jwt-decode`)
|
|
**Tests**: Tokens expirés rejetés
|
|
|
|
---
|
|
|
|
#### PR #8: Tests & Mocks
|
|
**Titre**: `feat: Add MSW server and fix test imports`
|
|
**Fichiers**:
|
|
- `src/mocks/server.ts` (nouveau)
|
|
- `src/pages/Dashboard/__tests__/Dashboard.test.tsx` (fix import)
|
|
**Tests**: Tests passent
|
|
|
|
---
|
|
|
|
#### PR #9: Error Handling
|
|
**Titre**: `feat: Add ErrorBoundary and API service with timeout/retry`
|
|
**Fichiers**:
|
|
- `src/components/ErrorBoundary.tsx` (nouveau)
|
|
- `src/services/api.ts` (nouveau)
|
|
- `App.tsx` (wrapper ErrorBoundary)
|
|
**Tests**: Erreurs gérées gracieusement
|
|
|
|
---
|
|
|
|
#### PR #10: Performance
|
|
**Titre**: `perf: Add memoization and lazy loading`
|
|
**Fichiers**:
|
|
- `src/screens/DashboardScreen.tsx` (memoization)
|
|
- `App.tsx` (lazy loading)
|
|
- `src/store/selectors.ts` (nouveau)
|
|
**Tests**: Performance améliorée mesurée
|
|
|
|
---
|
|
|
|
## 📊 RÉSUMÉ EXÉCUTIF
|
|
|
|
### Statut Global: 🔴 **CRITIQUE**
|
|
|
|
**Problèmes Identifiés**: 35+ (8 P0, 10 P1, 10 P2, 7 P3)
|
|
|
|
**Temps Estimé de Correction**:
|
|
- **P0**: 15-20h (1-2 sprints)
|
|
- **P1**: 12-15h (1 sprint)
|
|
- **P2**: 15-20h (1-2 sprints)
|
|
- **P3**: 5-8h (0.5 sprint)
|
|
|
|
**Total**: ~50-65h (3-4 sprints de 2 semaines)
|
|
|
|
### Risques Majeurs
|
|
|
|
1. ⚠️ **BUILD IMPOSSIBLE** — Module ne peut pas être compilé
|
|
2. ⚠️ **SÉCURITÉ CRITIQUE** — Tokens en clair, auth bypass
|
|
3. ⚠️ **CRASH GARANTI** — Imports manquants
|
|
4. ⚠️ **INCOMPATIBILITÉ** — Code web dans app React Native
|
|
|
|
### Recommandations Immédiates
|
|
|
|
1. **URGENT**: Créer configuration de base (package.json, tsconfig, app.json)
|
|
2. **URGENT**: Créer screens et slices manquants
|
|
3. **URGENT**: Fix authentification hardcodée
|
|
4. **URGENT**: Chiffrer tokens JWT
|
|
5. **HAUTE**: Réécrire composants UI en React Native
|
|
6. **HAUTE**: Remplacer Sentry web par React Native
|
|
|
|
### Prochaines Étapes
|
|
|
|
1. Valider ce rapport avec l'équipe
|
|
2. Prioriser les P0 en fonction du contexte business
|
|
3. Créer les issues/tickets correspondants
|
|
4. Commencer par PR #1 (Configuration de base)
|
|
5. Tester chaque PR avant merge
|
|
|
|
---
|
|
|
|
**Fin du Rapport d'Audit**
|
|
|