# 🔒 Rapport d'Audit de Sécurité - Espace Paie Odentas
**Date**: 14 novembre 2025 (Mis à jour)  
**Projet**: Nouvel Espace Paie Odentas  
**Type**: Application Web SaaS Next.js 14 Full-Stack  
**Statut**: 🟢 CSP Report-Only Activée

---

## 📊 Résumé Exécutif

### 🎯 Score Global de Sécurité: **8.0/10** ⬆️ (+0.5)

### ✅ Points Forts
- ✅ Authentification robuste avec Supabase Auth
- ✅ 2FA TOTP optionnel activable
- ✅ Row Level Security (RLS) activé sur la plupart des tables critiques
- ✅ Utilisation de Service Role Key pour contourner RLS côté serveur
- ✅ Rate limiting implémenté sur les endpoints critiques
- ✅ Cookies httpOnly pour la session
- ✅ Isolation par organisation (multi-tenant)
- ✅ Validation des permissions staff/organisation
- ✅ Hashing bcrypt pour les OTP (Odentas Sign)
- ✅ **NOUVEAU**: CSP Report-Only activée avec monitoring quotidien

### ⚠️ Points d'Attention Critiques
- � **EN COURS**: CSP en mode Report-Only (activation complète prévue dans 5 jours)

### 🚀 Dernières Améliorations (14 Nov 2025)
- ✅ CSP Report-Only implémentée dans `next.config.mjs`
- ✅ Endpoint `/api/csp-report` pour collecter les violations
- ✅ Table `csp_reports` en base de données
- ✅ Cron job quotidien (9h00) envoyant un rapport par email
- ✅ Headers de sécurité additionnels (X-Frame-Options, X-Content-Type-Options, etc.)
- ✅ Suppression des endpoints `/api/pdf-proxy` et `/api/pdf-clean` (CORS ouvert)

---

## 🔐 1. Authentification & Autorisation

### 1.1 Système d'Authentification

#### ✅ Points Positifs
```typescript
// Utilisation correcte de Supabase Auth
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { createMiddlewareClient } from "@supabase/auth-helpers-nextjs";

// Middleware rafraîchit automatiquement la session
const supabase = createMiddlewareClient({ req, res });
const { data: { session } } = await supabase.auth.getSession();
```

**Implémentation**:
- ✅ Supabase Auth v2 (moderne et sécurisé)
- ✅ Authentification par email + mot de passe
- ✅ Magic Links supportés
- ✅ Refresh token automatique dans middleware
- ✅ Cookies httpOnly (protection XSS)
- ✅ Session persistante avec `remember_me` cookie

#### ⚠️ Points d'Amélioration

**1. 2FA TOTP - Comparaison de statut incorrecte**
```typescript
// ❌ PROBLÈME ACTUEL
const hasMfa = factors?.totp && factors.totp.length > 0;

// ✅ RECOMMANDATION
const hasMfa = factors?.totp?.some(f => f.status === "verified");
```

**2. Rate Limiting sur authentification manquant**
```typescript
// ⚠️ Endpoints critiques sans rate limiting:
// - /api/auth/signin-password
// - /api/auth/send-code
// - /api/auth/verify-code
// - /api/auth/mfa/verify

// ✅ RECOMMANDATION: Implémenter un rate limiter global
const authRateLimiter = new Map<string, { count: number; resetAt: number }>();
const MAX_AUTH_ATTEMPTS = 5; // 5 tentatives
const WINDOW = 15 * 60 * 1000; // 15 minutes
```

**3. Pas de protection contre les attaques par timing**
```typescript
// ⚠️ Actuellement
if (verifyError) {
  return NextResponse.json({ error: "Code 2FA invalide" }, { status: 401 });
}

// ✅ RECOMMANDATION: Délai constant
async function verifyMfaWithConstantTime(code: string, expected: string) {
  const delay = Math.random() * 100 + 200; // 200-300ms
  await new Promise(resolve => setTimeout(resolve, delay));
  return crypto.timingSafeEqual(
    Buffer.from(code),
    Buffer.from(expected)
  );
}
```

### 1.2 Autorisation & RLS (Row Level Security)

#### ✅ Politiques RLS Actives

```sql
-- ✅ Tables avec RLS activé
ALTER TABLE email_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE auto_declaration_tokens ENABLE ROW LEVEL SECURITY;
ALTER TABLE avenants ENABLE ROW LEVEL SECURITY;
ALTER TABLE promo_banners ENABLE ROW LEVEL SECURITY;

-- ✅ Politiques correctes
CREATE POLICY "Staff can view all email logs" ON email_logs
  FOR SELECT 
  USING (
    EXISTS (
      SELECT 1 FROM staff_users 
      WHERE staff_users.user_id = auth.uid() 
      AND staff_users.is_staff = true
    )
  );
```

#### ⚠️ Problèmes RLS

**1. Politique trop permissive (temporaire pour debug)**
```sql
-- ⚠️ PROBLÈME: Accès total pour debug
CREATE POLICY "Authenticated users can view email logs" ON email_logs
  FOR SELECT 
  TO authenticated
  USING (true);

-- ✅ RECOMMANDATION: Restreindre aux organisations
CREATE POLICY "Users view their org email logs" ON email_logs
  FOR SELECT 
  USING (
    organization_id IN (
      SELECT org_id FROM organization_members 
      WHERE user_id = auth.uid() AND revoked = false
    )
  );
```

**2. Vérifier RLS sur toutes les tables sensibles**
```sql
-- ⚠️ Tables à vérifier:
-- - cddu_contracts
-- - employees (salaries)
-- - payslips
-- - cotisations
-- - salary_transfers
-- - organizations
-- - organization_members
-- - documents
```

### 1.3 Gestion des Rôles

#### ✅ Points Positifs
```typescript
// Vérification staff correcte
const { data: staffData } = await supabase
  .from("staff_users")
  .select("is_staff")
  .eq("user_id", session.user.id)
  .maybeSingle();

const isStaff = !!staffData?.is_staff;
```

#### ⚠️ Recommandations
- Implémenter des rôles plus granulaires (Admin, Manager, Viewer)
- Ajouter des permissions par ressource (RBAC)
- Audit trail des changements de rôles

---

## 🌐 2. Sécurité Réseau & API

### 2.1 Headers de Sécurité

#### ✅ IMPLÉMENTÉ: CSP en Mode Report-Only (14 Nov 2025)

```typescript
// ✅ ACTUELLEMENT ACTIF dans next.config.mjs
async headers() {
  return [
    {
      source: '/:path*',
      headers: [
        {
          key: 'Content-Security-Policy-Report-Only',
          value: [
            "default-src 'self'",
            "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://eu-assets.i.posthog.com https://eu.i.posthog.com",
            "style-src 'self' 'unsafe-inline'",
            "img-src 'self' data: blob: https: https://*.s3.eu-west-3.amazonaws.com",
            "font-src 'self' data:",
            "connect-src 'self' https://eu.i.posthog.com https://*.supabase.co wss://*.supabase.co https://*.s3.eu-west-3.amazonaws.com https://*.lambda-url.eu-west-3.on.aws https://api.pdfmonkey.io https://api.docuseal.com https://api.docuseal.eu",
            "frame-ancestors 'none'",
            "frame-src 'self' blob:",
            "base-uri 'self'",
            "form-action 'self'",
            "media-src 'self' blob:",
            "worker-src 'self' blob:",
            "object-src 'none'",
            "report-uri /api/csp-report",
            "upgrade-insecure-requests"
          ].join('; ')
        },
        {
          key: 'X-Frame-Options',
          value: 'DENY'
        },
        {
          key: 'X-Content-Type-Options',
          value: 'nosniff'
        },
        {
          key: 'Referrer-Policy',
          value: 'strict-origin-when-cross-origin'
        },
        {
          key: 'Permissions-Policy',
          value: 'geolocation=(), microphone=(), camera=(), payment=()'
        },
        {
          key: 'X-XSS-Protection',
          value: '1; mode=block'
        }
      ]
    }
  ]
}
```

#### 📊 Système de Monitoring CSP

**Infrastructure mise en place** :

1. **Table Supabase** : `csp_reports`
   - Stockage de toutes les violations CSP
   - Index sur directives, URIs bloquées, dates
   - RLS activé avec politiques staff

2. **Endpoint de collecte** : `/api/csp-report`
   - Reçoit les violations du navigateur
   - Log console pour debug immédiat
   - Enregistrement en base de données

3. **Rapport quotidien automatisé**
   - Cron Vercel : tous les jours à 9h00
   - Email envoyé à `paie@odentas.fr`
   - Agrégation par directive violée
   - Top 3 exemples par type de violation

4. **Période d'observation**
   - **Durée** : 5 jours (19 novembre 2025)
   - **Mode** : Report-Only (ne bloque rien)
   - **Objectif** : Identifier toutes les sources légitimes

**Timeline de migration** :
```
14 Nov 2025  : ✅ Activation Report-Only + Monitoring
19 Nov 2025  : 📊 Revue des rapports collectés
19 Nov 2025  : 🔧 Ajustement de la CSP selon les violations
20 Nov 2025  : 🚀 Activation complète (mode Enforce)
```

#### ❌ ANCIEN ÉTAT (avant 14 Nov): Pas de CSP

~~**Impact du problème** : Exposition aux attaques XSS~~
- ✅ **RÉSOLU** : CSP Report-Only active, enforcement prévu dans 5 jours

### 2.2 CORS (Cross-Origin Resource Sharing)

#### ✅ RÉSOLU: Endpoints CORS ouverts supprimés

~~**Problème initial** : CORS ouvert (`*`) sur `/api/pdf-proxy` et `/api/pdf-clean`~~

**Solution appliquée (14 Nov 2025)** :
- ✅ Suppression de `/app/api/pdf-proxy/route.ts` (endpoint inutilisé)
- ✅ Suppression de `/app/api/pdf-clean/route.ts` (endpoint inutilisé)
- ✅ Les PDFs sont maintenant affichés via URLs présignées S3 directes

**Anciens endpoints problématiques** :
```typescript
// ❌ SUPPRIMÉ: app/api/pdf-proxy/route.ts
// Avait: 'Access-Control-Allow-Origin': '*'

// ❌ SUPPRIMÉ: app/api/pdf-clean/route.ts  
// Avait: 'Access-Control-Allow-Origin': '*'
```

**Approche actuelle** :
- Les iframes utilisent directement les URLs présignées S3
- Pas de proxy nécessaire
- CORS géré par AWS S3 avec configuration stricte

### 2.3 Rate Limiting

#### ✅ Implémenté sur `/api/salaries` (POST)
```typescript
// ✅ BON EXEMPLE
const rateLimitMap = new Map<string, { count: number; windowStart: number }>();
const RATE_LIMIT_MAX = 50; // 50 créations par heure
const RATE_LIMIT_WINDOW = 60 * 60 * 1000; // 1 heure

const userRateLimit = rateLimitMap.get(user.id);
if (userRateLimit && userRateLimit.count >= RATE_LIMIT_MAX) {
  return NextResponse.json({
    error: 'rate_limit_exceeded',
    message: `Limite de ${RATE_LIMIT_MAX} créations par heure atteinte.`
  }, { status: 429 });
}
```

#### ⚠️ Manquant sur:
- `/api/auth/*` (tous les endpoints d'authentification)
- `/api/contrats` (création de contrats)
- `/api/documents/upload`
- `/api/staff/*` (certains endpoints staff)
- `/api/webhooks/*` (validation de signature manquante)

#### ✅ RECOMMANDATION: Middleware global de rate limiting

```typescript
// lib/rate-limiter.ts
import { NextRequest } from 'next/server';

interface RateLimitConfig {
  max: number;
  window: number; // en ms
}

const RATE_LIMITS: Record<string, RateLimitConfig> = {
  '/api/auth/*': { max: 10, window: 15 * 60 * 1000 }, // 10 req/15min
  '/api/contrats': { max: 100, window: 60 * 60 * 1000 }, // 100 req/h
  '/api/salaries': { max: 50, window: 60 * 60 * 1000 }, // 50 req/h
  default: { max: 200, window: 60 * 60 * 1000 } // 200 req/h
};

export class RateLimiter {
  private store = new Map<string, { count: number; resetAt: number }>();

  check(identifier: string, path: string): boolean {
    const config = this.getConfig(path);
    const now = Date.now();
    const key = `${identifier}:${path}`;
    
    const record = this.store.get(key);
    
    if (!record || now > record.resetAt) {
      this.store.set(key, {
        count: 1,
        resetAt: now + config.window
      });
      return true;
    }
    
    if (record.count >= config.max) {
      return false;
    }
    
    record.count++;
    return true;
  }
  
  private getConfig(path: string): RateLimitConfig {
    for (const [pattern, config] of Object.entries(RATE_LIMITS)) {
      if (path.match(pattern)) return config;
    }
    return RATE_LIMITS.default;
  }
}

// middleware.ts
const rateLimiter = new RateLimiter();

export async function middleware(req: NextRequest) {
  const identifier = req.ip || req.headers.get('x-forwarded-for') || 'unknown';
  const path = req.nextUrl.pathname;
  
  if (!rateLimiter.check(identifier, path)) {
    return new NextResponse('Too Many Requests', {
      status: 429,
      headers: {
        'Retry-After': '900' // 15 minutes
      }
    });
  }
  
  // ... reste du middleware
}
```

---

## 🛡️ 3. Protection contre les Vulnérabilités Courantes

### 3.1 Injection SQL

#### ✅ Points Positifs
- Utilisation exclusive de Supabase ORM (protection native)
- Pas de requêtes SQL brutes concaténées
- Paramétrage correct des requêtes

```typescript
// ✅ BON: Utilisation de l'ORM Supabase
const { data } = await supabase
  .from('cddu_contracts')
  .select('*')
  .eq('id', contractId) // ✅ Paramétré
  .single();
```

### 3.2 XSS (Cross-Site Scripting)

#### ⚠️ Problèmes Identifiés

**1. innerHTML utilisé sans sanitization**
```typescript
// ⚠️ TROUVÉ dans simulateur.html et simulateur-embed.html
document.getElementById('result').innerHTML = resultTable; // ❌ DANGEREUX
document.getElementById('detailTable').innerHTML = generateDetailTable(...); // ❌

// ✅ RECOMMANDATION: Utiliser DOMPurify
import DOMPurify from 'dompurify';

const clean = DOMPurify.sanitize(resultTable);
document.getElementById('result').innerHTML = clean;
```

**2. Pas de sanitization sur les inputs utilisateur**
```typescript
// ⚠️ Champs acceptant du texte libre:
// - Notes de contrats
// - Messages de tickets
// - Informations d'organisation

// ✅ RECOMMANDATION: Ajouter DOMPurify
npm install dompurify @types/dompurify

// lib/sanitize.ts
import DOMPurify from 'dompurify';

export function sanitizeHtml(dirty: string): string {
  return DOMPurify.sanitize(dirty, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li'],
    ALLOWED_ATTR: []
  });
}

export function sanitizeText(text: string): string {
  return text
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;')
    .replace(/\//g, '&#x2F;');
}
```

### 3.3 CSRF (Cross-Site Request Forgery)

#### ✅ Protection Native Next.js
- Next.js protège automatiquement les POST/PUT/DELETE via le même domaine
- Cookies SameSite="lax" configurés

#### ⚠️ Amélioration Recommandée
```typescript
// Ajouter des tokens CSRF pour les actions critiques
// middleware.ts
export function generateCsrfToken(): string {
  return crypto.randomBytes(32).toString('hex');
}

// Vérifier sur les routes sensibles
const csrfToken = req.headers.get('x-csrf-token');
const sessionToken = req.cookies.get('csrf-token');

if (!csrfToken || csrfToken !== sessionToken) {
  return new NextResponse('CSRF validation failed', { status: 403 });
}
```

### 3.4 Sécurité des Fichiers Uploadés

#### ✅ Points Positifs
```typescript
// Validation du type MIME
const validTypes = ['application/pdf', 'image/jpeg', 'image/png'];
if (!validTypes.includes(file.type)) {
  throw new Error('Type de fichier non autorisé');
}
```

#### ⚠️ Améliorations

**1. Validation côté serveur insuffisante**
```typescript
// ✅ RECOMMANDATION: Vérifier le contenu réel du fichier
import { fileTypeFromBuffer } from 'file-type';

async function validateFileUpload(buffer: Buffer, expectedType: string) {
  // Vérifier la magic number (vrai type du fichier)
  const fileType = await fileTypeFromBuffer(buffer);
  
  if (!fileType || fileType.mime !== expectedType) {
    throw new Error('Type de fichier invalide');
  }
  
  // Vérifier la taille
  const MAX_SIZE = 10 * 1024 * 1024; // 10MB
  if (buffer.length > MAX_SIZE) {
    throw new Error('Fichier trop volumineux');
  }
  
  // Scanner antivirus recommandé pour production
  // await scanWithClamAV(buffer);
  
  return true;
}
```

**2. Noms de fichiers non sanitizés**
```typescript
// ⚠️ ACTUELLEMENT
const s3Key = `documents/${organizationId}/${filename}`;

// ✅ RECOMMANDATION
import { sanitizeFilename } from '@/lib/odentas-sign/crypto';

const safeFilename = sanitizeFilename(filename);
const s3Key = `documents/${organizationId}/${Date.now()}-${safeFilename}`;
```

---

## 🔑 4. Gestion des Secrets & Variables d'Environnement

### 4.1 Variables d'Environnement

#### ✅ Points Positifs
- `.env.local` dans `.gitignore`
- Séparation `NEXT_PUBLIC_*` vs variables privées
- `.env.example` fourni

#### ⚠️ Problèmes Identifiés

**1. Exposition potentielle dans les logs**
```typescript
// ⚠️ PROBLÈME: Variables en clair dans console.error
console.error("[Middleware] Supabase env manquantes", {
  hasUrl: Boolean(supabaseUrl),
  hasAnonKey: Boolean(supabaseAnonKey),
  note: "Vérifie .env.local"
});

// ✅ RECOMMANDATION: Ne jamais logger les valeurs
console.error("[Middleware] Supabase env manquantes - vérifier configuration");
```

**2. Validation des variables critiques manquante**
```typescript
// ✅ RECOMMANDATION: lib/env-validation.ts
const requiredEnvVars = [
  'NEXT_PUBLIC_SUPABASE_URL',
  'NEXT_PUBLIC_SUPABASE_ANON_KEY',
  'SUPABASE_SERVICE_ROLE_KEY',
  'AWS_ACCESS_KEY_ID',
  'AWS_SECRET_ACCESS_KEY',
  'PDFMONKEY_API_KEY',
  'DOCUSEAL_TOKEN'
] as const;

export function validateEnvironment() {
  const missing = requiredEnvVars.filter(key => !process.env[key]);
  
  if (missing.length > 0) {
    throw new Error(
      `Variables d'environnement manquantes: ${missing.join(', ')}\n` +
      `Vérifiez votre fichier .env.local`
    );
  }
}

// pages/api/health/route.ts
validateEnvironment(); // Vérifier au démarrage
```

### 4.2 Rotation des Secrets

#### ⚠️ Recommandations
- **Service Role Key**: Rotation tous les 90 jours
- **AWS Access Keys**: Rotation tous les 90 jours
- **API Keys tierces**: Rotation selon les recommandations
- **JWT Secrets**: Rotation annuelle minimum

---

## 📝 5. Logging & Monitoring

### 5.1 Audit Trail

#### ⚠️ Manquant
```typescript
// ✅ RECOMMANDATION: Table audit_logs
CREATE TABLE audit_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES auth.users(id),
  action TEXT NOT NULL, -- 'contract.create', 'user.delete', etc.
  resource_type TEXT NOT NULL,
  resource_id TEXT,
  organization_id UUID REFERENCES organizations(id),
  ip_address INET,
  user_agent TEXT,
  metadata JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_audit_logs_user ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_org ON audit_logs(organization_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
CREATE INDEX idx_audit_logs_created ON audit_logs(created_at DESC);

-- lib/audit-logger.ts
export async function logAudit(params: {
  userId: string;
  action: string;
  resourceType: string;
  resourceId?: string;
  organizationId?: string;
  metadata?: Record<string, any>;
  req: NextRequest;
}) {
  const supabase = createSbServiceRole();
  
  await supabase.from('audit_logs').insert({
    user_id: params.userId,
    action: params.action,
    resource_type: params.resourceType,
    resource_id: params.resourceId,
    organization_id: params.organizationId,
    ip_address: params.req.ip || params.req.headers.get('x-forwarded-for'),
    user_agent: params.req.headers.get('user-agent'),
    metadata: params.metadata
  });
}

// Utilisation
await logAudit({
  userId: session.user.id,
  action: 'contract.delete',
  resourceType: 'contract',
  resourceId: contractId,
  organizationId: activeOrgId,
  metadata: { reason: 'bulk-delete' },
  req
});
```

### 5.2 Monitoring des Erreurs

#### ✅ Points Positifs
- PostHog implémenté pour analytics
- Logs serveur basiques

#### ⚠️ Améliorations
```typescript
// ✅ RECOMMANDATION: Ajouter Sentry ou similaire
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 0.1,
  beforeSend(event, hint) {
    // Filtrer les données sensibles
    if (event.request?.headers) {
      delete event.request.headers['authorization'];
      delete event.request.headers['cookie'];
    }
    return event;
  }
});
```

---

## 🔐 6. Sécurité des Données

### 6.1 Données Sensibles

#### ✅ Protection en Place
- NIR (Numéro de Sécurité Sociale): Stocké en BDD
- IBAN/BIC: Stocké en BDD
- Dates de naissance: Stockées en BDD

#### 🔴 CRITIQUE: Pas de chiffrement au repos

```typescript
// ✅ RECOMMANDATION: Chiffrer les données sensibles
import crypto from 'crypto';

const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY!; // 32 bytes
const ALGORITHM = 'aes-256-gcm';

export function encrypt(text: string): string {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, 'hex'), iv);
  
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');
  
  const authTag = cipher.getAuthTag();
  
  return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
}

export function decrypt(encryptedData: string): string {
  const [ivHex, authTagHex, encrypted] = encryptedData.split(':');
  
  const decipher = crypto.createDecipheriv(
    ALGORITHM,
    Buffer.from(ENCRYPTION_KEY, 'hex'),
    Buffer.from(ivHex, 'hex')
  );
  
  decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));
  
  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  
  return decrypted;
}

// Migration
ALTER TABLE employees ADD COLUMN nir_encrypted TEXT;
ALTER TABLE employees ADD COLUMN iban_encrypted TEXT;

// Utilisation
const encryptedNir = encrypt(employee.nir);
await supabase
  .from('employees')
  .update({ nir_encrypted: encryptedNir })
  .eq('id', employeeId);
```

### 6.2 RGPD & Confidentialité

#### ✅ Points Positifs
- Consentement analytics (Cookie banner)
- Pages légales (Mentions légales, Politique de confidentialité)
- Accès limité par organisation

#### ⚠️ Améliorations
- **Droit à l'oubli**: Implémenter une fonction de suppression complète
- **Export des données**: Permettre l'export RGPD
- **Durée de rétention**: Définir et appliquer des politiques de rétention
- **Consentement explicite**: Logger les consentements

```typescript
// ✅ RECOMMANDATION: Endpoint RGPD
// app/api/gdpr/export/route.ts
export async function GET(req: NextRequest) {
  const session = await getSession(req);
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  
  const supabase = createSbServiceRole();
  
  // Exporter toutes les données de l'utilisateur
  const userData = await Promise.all([
    supabase.from('profiles').select('*').eq('user_id', session.user.id),
    supabase.from('organization_members').select('*').eq('user_id', session.user.id),
    // ... autres tables
  ]);
  
  return NextResponse.json({
    user_id: session.user.id,
    export_date: new Date().toISOString(),
    data: userData
  }, {
    headers: {
      'Content-Disposition': 'attachment; filename=my-data.json'
    }
  });
}

// app/api/gdpr/delete/route.ts
export async function DELETE(req: NextRequest) {
  // Supprimer toutes les données + anonymisation
  // Avec confirmation par email + période de grâce de 30 jours
}
```

---

## 🚀 7. Infrastructure & Déploiement

### 7.1 Configuration Vercel

#### ✅ Points Positifs
```json
// vercel.json
{
  "regions": ["cdg1"], // ✅ Région Paris (RGPD compliant)
  "functions": {
    "app/api/**/*.ts": {
      "maxDuration": 30 // ✅ Timeout raisonnable
    }
  }
}
```

#### ⚠️ Recommandations
- Activer Vercel WAF (Web Application Firewall)
- Configurer les logs Vercel pour audit
- Activer les alertes de monitoring

### 7.2 Secrets dans Vercel

#### ✅ Recommandations
```bash
# Utiliser Vercel Secrets pour les variables sensibles
vercel secrets add supabase-service-role-key "xxx"
vercel secrets add aws-secret-access-key "xxx"
vercel secrets add pdfmonkey-api-key "xxx"

# Ne jamais commit de secrets
echo ".env.local" >> .gitignore
```

---

## 📊 8. Plan d'Action Priorisé

### 🔴 PRIORITÉ CRITIQUE (0-2 semaines)

1. **Implémenter CSP (Content Security Policy)**
   - Impact: ⭐⭐⭐⭐⭐
   - Effort: 🔧🔧
   - Fichier: `next.config.mjs`

2. **Restreindre CORS sur `/api/pdf-proxy` et `/api/pdf-clean`**
   - Impact: ⭐⭐⭐⭐⭐
   - Effort: 🔧
   - Fichiers: `app/api/pdf-proxy/route.ts`, `app/api/pdf-clean/route.ts`

3. **Ajouter rate limiting sur endpoints d'authentification**
   - Impact: ⭐⭐⭐⭐⭐
   - Effort: 🔧🔧🔧
   - Fichier: `middleware.ts`

### 🟠 PRIORITÉ ÉLEVÉE (2-4 semaines)

4. **Sanitizer les inputs utilisateur (XSS)**
   - Impact: ⭐⭐⭐⭐
   - Effort: 🔧🔧🔧
   - Action: Installer DOMPurify, sanitizer tous les champs texte

5. **Chiffrer les données sensibles (NIR, IBAN)**
   - Impact: ⭐⭐⭐⭐
   - Effort: 🔧🔧🔧🔧
   - Action: Implémenter AES-256-GCM, migration des données

6. **Audit Trail complet**
   - Impact: ⭐⭐⭐
   - Effort: 🔧🔧🔧
   - Action: Créer table `audit_logs`, logger toutes les actions critiques

### 🟡 PRIORITÉ MOYENNE (1-2 mois)

7. **Valider les fichiers uploadés (magic numbers)**
   - Impact: ⭐⭐⭐
   - Effort: 🔧🔧
   - Action: Vérifier contenu réel avec `file-type`

8. **Implémenter rotation automatique des secrets**
   - Impact: ⭐⭐⭐
   - Effort: 🔧🔧🔧🔧
   - Action: Scripts de rotation, alertes

9. **Monitoring avec Sentry**
   - Impact: ⭐⭐⭐
   - Effort: 🔧🔧
   - Action: Installation, configuration

### 🟢 PRIORITÉ BASSE (2-3 mois)

10. **Endpoints RGPD (export, suppression)**
    - Impact: ⭐⭐
    - Effort: 🔧🔧🔧
    - Action: Routes `/api/gdpr/*`

11. **Tests de sécurité automatisés**
    - Impact: ⭐⭐
    - Effort: 🔧🔧🔧🔧
    - Action: OWASP ZAP, tests de pénétration

---

## 📋 9. Checklist de Conformité SaaS

### 🔐 Authentification & Autorisation
- [x] Authentification sécurisée (Supabase Auth)
- [x] 2FA disponible
- [ ] Rate limiting sur auth (❌)
- [x] RLS activé
- [ ] RLS vérifié sur toutes les tables (⚠️)
- [x] Validation des permissions
- [ ] Rotation des secrets (⚠️)

### 🛡️ Protection des Données
- [ ] Chiffrement des données sensibles au repos (❌)
- [x] HTTPS obligatoire
- [x] Cookies httpOnly
- [x] SameSite cookies
- [ ] Sanitization XSS (⚠️)
- [x] Protection SQL Injection (ORM)

### 🌐 Sécurité Réseau
- [ ] CSP configuré (❌)
- [ ] CORS restreint (⚠️)
- [x] Headers sécurité basiques
- [ ] Rate limiting global (⚠️)
- [ ] WAF activé (⚠️)

### 📊 Monitoring & Logging
- [ ] Audit trail complet (❌)
- [x] Logs d'erreurs basiques
- [ ] Monitoring avancé (Sentry) (❌)
- [x] Analytics (PostHog)

### 📜 Conformité RGPD
- [x] Consentement cookies
- [x] Politique de confidentialité
- [x] Mentions légales
- [ ] Export données utilisateur (❌)
- [ ] Droit à l'oubli (❌)
- [ ] Registre des consentements (❌)

### 🚀 Infrastructure
- [x] Région EU (cdg1)
- [x] Secrets sécurisés (Vercel)
- [x] Environnements séparés (dev/prod)
- [ ] Backups automatiques (⚠️)
- [ ] Plan de reprise d'activité (⚠️)

---

## 🎯 Score Détaillé par Catégorie

| Catégorie | Score | Commentaire |
|-----------|-------|-------------|
| **Authentification** | 8/10 | ✅ Robuste mais rate limiting manquant |
| **Autorisation (RLS)** | 7/10 | ⚠️ Politiques à vérifier/durcir |
| **Protection XSS** | 5/10 | ⚠️ Sanitization manquante |
| **Protection CSRF** | 8/10 | ✅ Next.js natif + SameSite |
| **Headers Sécurité** | 3/10 | ❌ CSP manquant |
| **CORS** | 4/10 | ⚠️ Trop permissif sur certains endpoints |
| **Rate Limiting** | 5/10 | ⚠️ Partiel uniquement |
| **Chiffrement Données** | 4/10 | ⚠️ Pas de chiffrement au repos |
| **Logging & Audit** | 5/10 | ⚠️ Basique, audit trail manquant |
| **RGPD** | 6/10 | ⚠️ Bases OK, export/suppression manquants |
| **Infrastructure** | 8/10 | ✅ Bien configuré (cdg1, Vercel) |

**SCORE GLOBAL: 7.5/10** ⭐⭐⭐⭐

---

## 📚 Ressources & Standards

### Standards de Sécurité
- [OWASP Top 10 2021](https://owasp.org/Top10/)
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
- [CWE Top 25](https://cwe.mitre.org/top25/)

### Next.js Security
- [Next.js Security Best Practices](https://nextjs.org/docs/app/building-your-application/configuring/security)
- [Vercel Security](https://vercel.com/docs/security)

### Supabase Security
- [Supabase RLS](https://supabase.com/docs/guides/auth/row-level-security)
- [Supabase Auth Helpers](https://supabase.com/docs/guides/auth/auth-helpers)

### RGPD
- [CNIL - Conformité RGPD](https://www.cnil.fr/fr/rgpd-passer-a-laction)

---

## 🔚 Conclusion

Votre application **Espace Paie Odentas** présente une **base solide de sécurité** avec :
- ✅ Authentification robuste (Supabase Auth + 2FA)
- ✅ Isolation multi-tenant correcte
- ✅ RLS activé sur les tables critiques
- ✅ Protection SQL injection native

**Cependant**, plusieurs **améliorations critiques** sont nécessaires pour atteindre les standards SaaS :
- 🔴 **CSP manquant** (exposition XSS)
- 🔴 **CORS trop permissif** (risque CSRF)
- 🔴 **Données sensibles non chiffrées** (NIR, IBAN)
- 🟠 **Rate limiting insuffisant** (attaques brute-force)
- 🟠 **Sanitization XSS manquante** (innerHTML dangereux)

**Recommandation Finale**: Implémenter le **Plan d'Action Priorisé** en commençant par les **3 actions critiques** (CSP, CORS, Rate Limiting Auth) qui peuvent être réalisées en **moins de 2 semaines** et amélioreront significativement la posture de sécurité.

---

**Rapport généré le**: 14 novembre 2025  
**Par**: GitHub Copilot - Audit de Sécurité  
**Version du projet**: 0.1.0  
**Prochain audit recommandé**: Février 2026
