Aller au contenu

PD-106 — Revue de sécurité (agent-adversarial)

Date : 2026-02-04 Scope : Code produit pour PD-106 (settingsApi, hooks, screens, components)

1. Secrets Management (INV-106-08)

Constat

  • settingsApi.ts : Aucun console.log ne contient de secret (mot de passe, code TOTP, codes de récupération). Vérifié sur les fonctions reauthenticate(), verifyTotpCode(), changePassword().
  • MfaEnrollScreen.tsx : Le secret TOTP et l'URI otpauth sont stockés dans un useState local. Le useEffect cleanup purge les données au unmount via clearEnrollData() et clearRecoveryCodes().
  • RecoveryCodesDisplay.tsx : Les codes sont reçus en props (éphémères). Aucune persistance.
  • ReauthModal.tsx : Le mot de passe est dans un useState local, réinitialisé au close (useEffect sur visible).
  • useReauth.ts : Le mot de passe n'est jamais stocké dans le hook — il est passé directement à l'API.

Verdict : CONFORME (INV-106-08)

Points de vigilance

  • Aucun appel SecureStore.setItemAsync() ou AsyncStorage.setItem() pour les secrets TOTP/recovery.
  • Aucun console.log ne contient de variable sensible.
  • Les tests TC-INV-01 vérifient l'absence de sentinelles dans le stockage.

2. Re-authentication (INV-106-05, INV-106-06)

Constat

  • useReauth.ts :
  • TTL 5 min vérifié via Date.now() - reauthTimestampRef.current < REAUTH_TTL_MS.
  • consume() marque la re-auth comme consommée (one-time via consumedRef).
  • invalidate() réinitialise complètement l'état.
  • Écrans (MfaSettingsScreen, ChangePasswordScreen, DeleteAccountScreen) : Chaque écran appelle navigation.addListener("blur", reauth.invalidate) — invalidation sur navigation hors flux.
  • Le mot de passe transit uniquement via onSubmit callback → settingsApi.reauthenticate() → fetch. Pas de stockage intermédiaire.

Verdict : CONFORME (INV-106-05, INV-106-06)

Risque résiduel

  • Le TTL client est un garde-fou UX. Le serveur fait autorité (conformément au plan section 7.3).

3. Confirmation renforcée (INV-106-14)

Constat

  • DeleteAccountScreen.tsx implémente 3 barrières séquentielles :
  • Saisie du mot "SUPPRIMER" (comparaison case-insensitive).
  • ConfirmationModal avec confirmation finale.
  • ReauthModal pour re-authentification.
  • Les 3 étapes sont séquentielles et non court-circuitables.
  • La comparaison utilise toUpperCase() pour être insensible à la casse.

Verdict : CONFORME (INV-106-14)

4. Navigation reset après opérations destructives (INV-106-13, INV-106-15)

Constat

  • SettingsScreen.tsx (logout) : clearAllOnLogout() + CommonActions.reset() vers Login.
  • DeleteAccountScreen.tsx : secureDelete() des tokens + CommonActions.reset() vers Login.
  • ChangePasswordScreen.tsx : clearAllOnLogout() + CommonActions.reset() vers Login.
  • L'utilisation de CommonActions.reset() dépile intégralement la navigation stack — aucun écran Settings ne reste en mémoire.

Verdict : CONFORME (INV-106-13, INV-106-15)

Point d'attention

  • DeleteAccountScreen utilise secureDelete() unitaire au lieu de clearAllOnLogout(). Recommandation : utiliser clearAllOnLogout() pour garantir un nettoyage complet.

5. Erreurs non silencieuses (INV-106-16)

Constat

  • Chaque opération critique (useMfa, ChangePasswordScreen, DeleteAccountScreen, SettingsScreen logout) utilise Alert.alert() pour afficher les erreurs.
  • Le pattern try/catch → Alert est systématique.
  • Les codes erreur API sont propagés via result.error sans exposition de données techniques internes.

Verdict : CONFORME (INV-106-16)

6. Injection / XSS

Constat

  • Les traductions i18n utilisent {{ }} interpolation (react-i18next) qui échappe par défaut.
  • Les données serveur (profil, codes erreur) sont rendues dans des <Text> React Native — pas de risque XSS dans ce contexte (pas de dangerouslySetInnerHTML).
  • Le QR code est affiché via <Image source={{ uri }} — pas d'exécution de script.

Verdict : CONFORME

7. Vulnérabilités identifiées

# Description Sévérité Recommandation
1 DeleteAccountScreen utilise secureDelete() unitaire au lieu de clearAllOnLogout() Faible Remplacer par clearAllOnLogout() pour cohérence
2 _handleSubmit exposé via cast as any sur useReauth — pattern fragile Info Refactorer pour exposer handleSubmit dans le type de retour
3 Absence de validation du format du code TOTP côté client (regex ^\d{6}$) Info Ajouter une validation regex avant appel API

8. Synthèse

Invariant Statut
INV-106-05 (re-auth avant critique) CONFORME
INV-106-06 (re-auth one-time) CONFORME
INV-106-08 (secrets non persistés) CONFORME
INV-106-09 (codes affichage unique) CONFORME
INV-106-13 (logout + nettoyage) CONFORME
INV-106-14 (confirmation renforcée) CONFORME
INV-106-15 (post-suppression) CONFORME avec réserve mineure
INV-106-16 (erreurs non silencieuses) CONFORME

Verdict global : CONFORME — 0 vulnérabilité critique, 3 recommandations mineures/info.