Aller au contenu

PD-32 — Security Review Report

Produit par agent-adversarial dans le cadre de l'étape 6b-5.

Résumé

Critère Statut
Forbidden patterns ✅ Aucune violation
Champs sensibles ✅ Protégés
Validation whitelist ✅ Actif
RLS enforcement ✅ Vérifié
Injection SQL ✅ Non vulnérable

Verdict global : CONFORME


1. Audit des Forbidden Patterns

1.1 user-profile-controller

Pattern interdit Recherché Résultat
Accès direct repository Repository<User> dans controller ❌ Absent (OK)
Route avec paramètre userId :userId, /:id ❌ Absent (OK)
Bypass des guards @Public(), skipAuth ❌ Absent (OK)

Code audité : user-profile.controller.ts - ✅ Utilise UserProfileService (pas d'accès direct) - ✅ Route /user/profile sans paramètre externe - ✅ @UseGuards(JwtAuthGuard) appliqué au niveau controller

1.2 user-profile-service

Pattern interdit Recherché Résultat
SQL brut concaténé query(\...${,${}` dans SQL ❌ Absent (OK)
Exposition champs sensibles passwordHash, srpSalt dans retour ❌ Absent (OK)
Modification champs protégés email =, plan =, status = ❌ Absent (OK)

Code audité : user-profile.service.ts - ✅ SQL paramétré uniquement : query('SET LOCAL app.current_user_id = $1', [userId]) - ✅ plainToInstance avec excludeExtraneousValues: true - ✅ Seuls name, avatarUrl, preferences sont modifiés

1.3 user-profile-dtos

Pattern interdit Recherché Résultat
Propriété sans validation Propriété sans @Is* ou @Expose ❌ Absent (OK)
Désactivation whitelist whitelist: false ❌ Absent (OK)
Champs protégés dans Update email, plan, id, status ❌ Absent (OK)

Code audité : *.dto.ts - ✅ Toutes les propriétés ont des décorateurs de validation - ✅ @ValidateNested() + @Type() sur preferences - ✅ Aucun champ protégé dans UpdateUserProfileDto


2. Vérification des Champs Sensibles

2.1 Entité User - Décorateurs @Exclude()

// Vérifié dans user.entity.ts
@Exclude() srpSalt      
@Exclude() passwordHash 
@Exclude() validationToken 

2.2 UserProfileResponseDto - Champs exposés

// Vérifié - uniquement ces champs sont exposés
@Expose() name        
@Expose() email       
@Expose() avatarUrl   
@Expose() preferences 

2.3 Test de fuite

Scénario : Appel GET /user/profile avec JWT valide Résultat attendu : Réponse ne contient que name, email, avatarUrl, preferences Méthode de vérification : Tests e2e TC-INV-32-03/04


3. Tentatives de Bypass

3.1 Injection de champs protégés via PUT

Payload malicieux Résultat attendu
{ "email": "hacker@evil.com" } 400 Bad Request
{ "plan": "business" } 400 Bad Request
{ "id": "uuid" } 400 Bad Request
{ "status": "ACTIVE" } 400 Bad Request
{ "passwordHash": "xxx" } 400 Bad Request

Mécanisme de protection : forbidNonWhitelisted: true dans ValidationPipe global

3.2 Accès croisé RLS

Scénario Mécanisme Résultat
User A GET profil B JWT.sub = A, RLS filtre sur A User A voit son propre profil
User A PUT profil B JWT.sub = A, RLS filtre sur A Seul profil A modifié

Protection : - SET LOCAL app.current_user_id = $1 avant chaque requête - Politiques RLS PostgreSQL sur vault_secure.users

3.3 Injection SQL

Vecteurs testés : - name: "'; DROP TABLE users; --" → Échappe via paramétrage TypeORM - preferences: { "locale": "'; SELECT * FROM users" } → Stocké en JSONB sans exécution

Résultat : Aucune injection possible (ORM paramétré)


4. Conformité INV-32-*

Invariant Description Vérifié
INV-32-01 JWT via AuthenticationGuard @UseGuards(JwtAuthGuard)
INV-32-02 Opérations via RLS RlsQueryService + SET LOCAL
INV-32-03 Response exclut sensibles @Exclude() sur entity
INV-32-04 passwordHash/srpSalt jamais exposés ✅ Testé e2e
INV-32-05 Update accepte uniquement name/avatar_url/preferences ✅ DTO whitelist
INV-32-06 Champs protégés rejetés forbidNonWhitelisted
INV-32-07 Preferences validé strictement @ValidateNested()
INV-32-08 GET retourne UserProfileResponseDto ✅ Controller type hint
INV-32-09 Rate limit sur endpoints ⚠️ Global (non spécifique)
INV-32-10 Transaction pour update dataSource.transaction()

Note INV-32-09 : Le rate limiting est géré au niveau global par l'application. Si un rate limiting spécifique est requis pour /user/profile, ajouter @UseGuards(RateLimitGuard).


5. Recommandations

5.1 Priorité haute

Aucune.

5.2 Priorité moyenne

  1. Rate limiting spécifique : Considérer un guard RateLimit spécifique pour les endpoints PUT /user/profile pour prévenir les abus de modification.

5.3 Priorité basse

  1. Audit logging : Ajouter un log d'audit pour les modifications de profil (AuditService.logEvent).
  2. Validation timezone : Valider que timezone est une timezone IANA valide (optionnel, actuellement string libre).

Conclusion

L'implémentation PD-32 respecte les exigences de sécurité définies dans les code contracts. Aucune vulnérabilité critique n'a été identifiée.

Approuvé : ✅ CONFORME


Review effectuée le 2026-02-05 par agent-adversarial