Aller au contenu

Session 6 — Chaos Métier, Logging Admin UI & Revue de code

Durée : ~12 heures (réparties sur plusieurs conversations Claude) Objectif initial : Implémenter le Chaos Métier (anomalies fonctionnelles e-commerce) Objectif final : 11 anomalies métier complètes + refonte UI admin logs + revue de code exhaustive + prompt de passation


🎯 Réalisations

1. Chaos Métier — 11 anomalies A1-A11

Temps : ~4h Difficulté : ⭐⭐⭐⭐

Quatrième couche de chaos après Backend, Frontend et Scripting. Contrairement aux trois précédents, le Chaos Métier injecte des anomalies dans la logique applicative — prix, stock, commandes, livraison — sans casser le parcours.

Architecture

BusinessChaosService.java est le service central. Il expose :

  • Des méthodes utilitaires appelées par les controllers (getTvaRate(), applyPriceRounding(), shouldDecrementStock(), processInputField()…)
  • Un activity log ring buffer de 200 entrées max
  • Des endpoints publics pour le monitoring (/api/chaos/public/business/*)

Les 11 anomalies par niveau

Niveau 1 — Junior (A1, A2, A3)

Anomalie Description Point d'injection
A1 TVA 19,6% au lieu de 20% OrderService.createOrderFromItems()
A2 Arrondi prix à l'entier inférieur si > 10€ OrderService.createOrderFromItems()
A3 Stock non décrémenté après commande OrderService.createOrderFromItems()

Niveau 2 — Confirmé (+ A4, A5, A6, A7)

Anomalie Description Point d'injection
A4 Email confirmation sans frais de port OrderService.createOrderFromItems()
A5 Double commande sur double-clic (pas d'idempotence) OrderService.createOrderFromItems()
A6 Code promo invalide accepté (0% silencieux) CheckoutController.applyPromo()
A7 Délai livraison en jours calendaires au lieu d'ouvrés OrderService + CheckoutController

Niveau 3 — Expert (+ A8, A9, A10, A11)

Anomalie Description Point d'injection
A8 Race condition stock sans verrou DB OrderService.createOrderFromItems()
A9 Injection de logs (LF/CRLF/${ sur champs libres) UserController + CheckoutController
A10 Total historique commandes faux (arrondi cumulé) OrderController.getUserOrders()
A11 Token session non invalidé 30s après logout AuthController.logout()

Breaking change — OrderController

Pour implémenter A10, GET /api/orders a été modifié pour retourner un objet enveloppé :

{
  \"orders\": [ ... ],
  \"historyTotal\": 299.97
}

Frontend adapté en conséquence : api.js déstructure le nouveau format, MyOrders.jsx affiche un badge \"Total dépensé\".

Injection SQL chaos (bonus niveau 2 et 3)

En plus des anomalies métier, des valeurs incorrectes sont injectées dans les champs de profil pour provoquer des erreurs SQL volontaires :

  • Niveau 2 : postal_code\"999999\" (dépasse VARCHAR(5)) → DataTruncation
  • Niveau 3 : postal_code\"9999999999\", country\"FRANCE\" (dépasse VARCHAR(2))

Difficultés :

  • A8 (race condition) : implémenter une lecture non atomique sans verrou en Java sans que Spring ne force la transactionnalité — nécessite un appel hors transaction au bon niveau
  • A9 (log poisoning) : la méthode processInputField() ne sanitise pas les caractères \, \\r, ${ sur les champs prénom, nom, ville, promo — injecte de fausses lignes de log
  • A11 : le flag graceActive exposé dans /api/auth/status pour que les scripts puissent détecter la fenêtre de 30 secondes post-logout

2. Chaos Scripting — évolution niveau 4 Maestro et améliorations

Temps : ~1h Difficulté : ⭐⭐⭐

Complétion du niveau 4 Maestro : la clé HMAC n'est plus statique mais dérivée du sessionToken de chaque utilisateur. Un hint 8 caractères est exposé dans X-Key-Hint pour aider le scripteur à identifier quelle clé utiliser.

Thread safety TokenBundle — correctif critique :

Les champs csrfToken, stepToken, signature, hmacKey étaient non volatile dans la première implémentation. Sous charge JMeter avec plusieurs utilisateurs simultanés, les threads pouvaient lire des valeurs stale depuis le cache du CPU. Solution : déclarer tous ces champs volatile.

Endpoints publics pour le monitoring (sans auth) :

GET /api/admin/chaos/scripting/public/status
GET /api/admin/chaos/scripting/public/logs

Ces endpoints ont été créés après avoir constaté que le monitoring appelait les endpoints admin (401 en boucle dans les logs). Le monitoring tab Scripting a été mis à jour pour utiliser uniquement les endpoints /public/.


3. Refonte UI Chaos Admin — Chaos Métier

Temps : ~1h30 Difficulté : ⭐⭐⭐

Le chaos-admin (public/index.html) a été étendu d'une quatrième section 💼 Chaos Métier avec :

  • 4 boutons de niveau (OFF / Junior / Confirmé / Expert)
  • Badge de niveau actif avec couleur (gris/vert/jaune/rouge)
  • Compteur d'anomalies actives
  • Tableau des 11 anomalies avec statut (✅ active / ⚫ inactive) selon le niveau

4. Monitoring — onglet Chaos Métier + Scripting

Temps : ~2h Difficulté : ⭐⭐⭐

Le dashboard monitoring (index.html) a été étendu de deux nouveaux onglets :

Onglet 📜 Scripting :

  • Niveau actif + statut en temps réel
  • Tableau des tokens actifs par session (email utilisateur, token tronqué avec tooltip)
  • Logs filtrables par niveau/type
  • Grid 5 colonnes : timestamp, utilisateur, action, step, résultat

Onglet 🎯 Chaos Métier :

  • Bannière niveau actif avec couleur
  • KPIs A1-A11 : compteur de déclenchements par anomalie
  • Logs filtrables avec sévérité (INFO/WARN/ERROR)
  • Badge par anomalie avec état actif/inactif

Correctif critique : le monitoring appelait /api/admin/chaos/scripting/logs (auth requise → 401). Remplacé par /api/admin/chaos/scripting/public/logs (sans auth).


5. Logging UI Admin — refonte affichage sessions

Temps : ~1h Difficulté : ⭐⭐

L'onglet logs du chaos-admin affichait des données brutes peu lisibles. Refonte en grille 5 colonnes :

Colonne Contenu
Timestamp Heure de l'événement
Utilisateur Email + avatar initial
Token 8 premiers caractères + tooltip (valeur complète au hover)
Action Type d'événement (login, add-to-cart, order…)
Résultat Badge coloré (✅ / ❌ / ⚠️)

L'affichage du securityToken a nécessité une réflexion sur la sécurité : valeur tronquée à 8 chars visible + valeur complète uniquement au survol (tooltip). Jamais dans les logs serveur.


6. Revue de code exhaustive & corrections

Temps : ~2h Difficulté : ⭐⭐⭐

Passe de relecture complète sur l'ensemble des fichiers modifiés. Correctifs appliqués :

HMAC — charset UTF-8 explicite (NAS Synology)

// AVANT — charset par défaut (platform-dependent)
key.getBytes()

// APRÈS — UTF-8 explicite
key.getBytes(StandardCharsets.UTF_8)
payload.getBytes(StandardCharsets.UTF_8)

Contexte : le NAS Synology tourne avec un charset système potentiellement différent de Windows. Sans StandardCharsets.UTF_8 explicite, le HMAC calculé côté NAS diffère de celui calculé par le script de test → E-SIG-07 systématique en production.

Thread safety TokenBundle — volatile

// AVANT
private String csrfToken;
private String stepToken;
private String signature;
private String hmacKey;

// APRÈS
private volatile String csrfToken;
private volatile String stepToken;
private volatile String signature;
private volatile String hmacKey;

Retry logic — OrderService

La logique de retry sur la création de commande avait un bug : en cas d'exception sur la première tentative, la deuxième tentative utilisait des données partiellement modifiées (prix déjà arrondis par A2, TVA déjà appliquée par A1). Correctif : recalcul depuis les données brutes sur chaque tentative.

Champ Product.stock

Plusieurs endroits du code utilisaient product.getStockQuantity() (champ inexistant). Le champ réel de l'entité Product s'appelle stock. Correction dans OrderService et dans les tests de déclenchement d'A3 et A8.


7. Génération du prompt de passation

Temps : ~30 min Difficulté : ⭐

En clôture de session, génération d'un prompt de passation exhaustif (PROMPT_REVUE_CODE_PERFSHOP.md) destiné à amorcer une revue de code complète en nouvelle conversation Claude.

Ce document contient : - L'architecture complète avec tous les endpoints - Les 4 systèmes chaos avec leur état exact - Les 11 anomalies métier avec points d'injection précis - La checklist de revue en 6 priorités - La règle d'or (ne pas corriger les anomalies intentionnelles) - Le format attendu du rapport de revue


🎢 Chronologie Détaillée

Phase 1 (~4h) : Chaos Métier — conception et implémentation 🟢

  • Définition des 11 anomalies et de leur point d'injection
  • BusinessChaosService.java — service central
  • BusinessChaosController.java — endpoints admin + publics
  • Breaking change OrderController pour A10
  • A11 : grace period 30s dans AuthController
  • Ressenti : 🟢 Architecture claire, chaque anomalie bien isolée

Phase 2 (~1h) : Scripting — niveau 4 Maestro + thread safety 🟢

  • Dérivation clé HMAC depuis sessionToken
  • X-Key-Hint dans les réponses
  • volatile sur TokenBundle
  • Endpoints publics scripting
  • Ressenti : 🟢 Correctifs propres, architecture solide

Phase 3 (~1h30) : UI chaos-admin — section Métier 🟢

  • 4 boutons de niveau
  • Tableau des 11 anomalies avec statut
  • Badge niveau coloré
  • Ressenti : 🟢 Interface cohérente avec les 3 sections existantes

Phase 4 (~2h) : Monitoring — onglets Scripting + Métier 🟡

  • Onglet Scripting : tokens sessions en grille 5 colonnes
  • Onglet Métier : KPIs A1-A11 + logs filtrables
  • Correctif endpoints publics (401 en boucle)
  • Ressenti : 🟡 Complexité HTML/JS, beaucoup de gestion d'état

Phase 5 (~2h) : Revue de code + correctifs 🟢

  • HMAC charset UTF-8
  • volatile TokenBundle
  • Retry logic OrderService
  • Champ stock vs stockQuantity
  • Ressenti : 🟢 Méthodique, chaque correctif justifié

Phase 6 (~30 min) : Prompt de passation 🟢

  • Synthèse complète de l'architecture
  • Format checklist revue de code
  • Ressenti : 🟢 Travail de documentation utile pour la suite

🧠 Moments Clés d'Apprentissage

1. Breaking change silencieux sur l'API

Wrapper GET /api/orders pour ajouter historyTotal (A10) sans versionner l'API. Le frontend s'adapte en déstructurant le nouvel objet. Un script JMeter ancien qui attendait un tableau brut reçoit maintenant un objet — comportement silencieusement différent.

Leçon : tout changement de format de réponse API doit être documenté même en contexte pédagogique.

2. Charset et HMAC sur NAS Synology

key.getBytes() en Java utilise le charset par défaut de la JVM, qui peut être ISO-8859-1 sur certains systèmes Linux anciens ou mal configurés. Sur le NAS Synology, le résultat HMAC différait de Windows → E-SIG-07 systématique.

Règle : toujours passer StandardCharsets.UTF_8 explicitement dans les opérations cryptographiques. Ne jamais assumer le charset par défaut.

3. volatile n'est pas synchronized

volatile garantit la visibilité entre threads (pas de valeur stale depuis le cache CPU) mais pas l'atomicité des opérations composées. Pour TokenBundle, volatile suffit : la rotation est une affectation simple d'une nouvelle valeur de référence.

Si on avait eu besoin d'un \"check-then-act\" (if (token == null) token = generate()), il aurait fallu synchronized ou un AtomicReference.

4. Endpoints publics vs endpoints admin dans le monitoring

Le monitoring (server.js) s'exécute sans cookie de session admin. Appeler les endpoints /api/admin/* depuis le monitoring génère des 401 en boucle — aucune erreur visible, juste des graphiques vides et des logs pollués.

Pattern correct : tout endpoint utile au monitoring doit avoir une version /public/ sans authentification, documentée séparément.

5. Race condition A8 sans invalidation du cache JPA

Implémenter une vraie race condition sur le stock sans verrou DB en Spring Boot : difficile car Spring Transaction + JPA tendent à lire depuis le first-level cache (EntityManager). Solution : forcer un flush() + lecture directe depuis le repository sur la vérification de stock, en dehors du contexte de transaction principal.


📊 Métriques de la Session

Métrique Valeur
Durée totale ~12 heures
Fichiers Java créés/modifiés 11
Fichiers frontend modifiés 3 (api.js, MyOrders.jsx, chaos-agent.js)
Fichiers chaos-admin modifiés 1 (index.html — section Métier)
Fichiers monitoring modifiés 1 (index.html — onglets Scripting + Métier)
Anomalies implémentées 11 (A1-A11)
Correctifs revue de code 4 (HMAC charset, volatile, retry, stock)
Migrations Flyway V26, V27, V28
Lignes de code ~1800 (Java + HTML + JS)
Bugs corrigés 6

🎯 Difficultés Rencontrées

🔴 Difficile (⭐⭐⭐⭐)

Problème : A8 race condition — Spring JPA tend à lire depuis le cache EntityManager
Temps perdu : ~30 min
Solution : Lecture de stock hors transaction principale via repository direct
Leçon : Les abstractions ORM protègent contre les race conditions — il faut activement les contourner pour simuler le comportement non sécurisé

🟡 Moyen (⭐⭐⭐)

Problème : Monitoring appelait /api/admin/chaos/scripting/logs (401 silencieux)
Temps perdu : ~20 min
Solution : Créer et utiliser /api/admin/chaos/scripting/public/logs
Leçon : Toujours vérifier l'authent requise avant d'ajouter un appel dans le monitoring

🟡 Moyen (⭐⭐⭐)

Problème : HMAC charset différent NAS vs Windows (découvert en revue de code)
Temps perdu : 0 (découvert préventivement)
Solution : StandardCharsets.UTF_8 explicite
Leçon : La revue de code préventive vaut plus que le debug post-production


✅ Ce Qui a Bien Fonctionné

Architecture Chaos Métier découplée

BusinessChaosService est un service transversal appelé par les controllers existants via des méthodes utilitaires atomiques. Les controllers ne savent pas si A1 ou A2 est actif — ils appellent businessChaos.getTvaRate() et reçoivent la valeur correcte ou incorrecte selon le niveau.

Pattern propre : aucun if (chaosLevel >= X) dispersé dans le code métier.

Activity log ring buffer

200 entrées max avec éviction automatique des plus anciennes. Pas de base de données, pas de fichier — en mémoire uniquement, cohérent avec la philosophie \"chaos éphémère\" du projet.

Prompt de passation comme outil de clôture

Générer un document de passation exhaustif en fin de session permet de : 1. Vérifier la cohérence de ce qui a été produit (l'écrire force à relire) 2. Préparer la session suivante sans avoir à \"réapprendre\" le projet 3. Fournir un contexte complet pour une revue de code externe


🎓 Conclusion

Cette session de ~12 heures en vibe coding a permis de :

Implémenter le Chaos Métier complet — 11 anomalies A1-A11 réparties sur 3 niveaux, toutes observables via le monitoring ✅ Compléter le Chaos Scripting — niveau 4 Maestro + thread safety volatile + endpoints publics monitoring ✅ Étendre le monitoring — 2 nouveaux onglets (Scripting, Métier) avec grilles de sessions et KPIs temps réel ✅ Corriger 4 problèmes critiques — HMAC charset, volatile, retry logic, champ stock ✅ Produire un prompt de passation — document exhaustif pour revue de code en nouvelle session

Ratio temps productif / temps total : ~80% Objectifs atteints : 100% (les 4 couches chaos sont opérationnelles)

La session a atteint un niveau de maturité du projet significatif : les 4 systèmes chaos (Backend, Frontend, Scripting, Métier) sont tous opérationnels, observables et documentés. La revue de code exhaustive a permis de corriger des bugs latents qui n'auraient causé des problèmes qu'en production sur le NAS — notamment le charset HMAC et le champ stock.

La leçon centrale de cette session : une revue de code systématique à la fin d'une session d'implémentation intense est un investissement qui se rentabilise immédiatement. Les 4 correctifs trouvés lors de cette revue auraient coûté chacun 30 à 60 minutes à diagnostiquer en production.


🤖 Erreurs imputables à Claude AI — Session 6


❌ Erreur S6-1 — key.getBytes() sans charset explicite

Ce que j'ai proposé : L'implémentation HMAC initiale dans ChaosScriptingService utilisait key.getBytes() et payload.getBytes() sans préciser StandardCharsets.UTF_8.

Ce qui s'est passé : En revue de code, identification du risque. Sur le NAS Synology, le charset par défaut de la JVM pouvait différer — le HMAC calculé aurait été différent de celui attendu par les scripts de test, causant E-SIG-07 systématique.

Pourquoi c'était une erreur : Toute opération cryptographique doit spécifier le charset explicitement. La règle StandardCharsets.UTF_8 dans les opérations crypto est fondamentale et non négociable — j'aurais dû l'inclure dès la première version.

Temps perdu : 0 (détecté en revue préventive) | Sévérité : ⭐⭐⭐⭐⭐ (aurait été critique en prod)


❌ Erreur S6-2 — TokenBundle sans volatile

Ce que j'ai proposé : La première implémentation de TokenBundle dans ChaosScriptingService avait csrfToken, stepToken, signature, hmacKey comme champs String normaux.

Ce qui s'est passé : Sous charge JMeter multi-utilisateurs, des threads auraient pu lire des valeurs stale depuis le cache CPU — visibility problem en Java multi-threadé.

Pourquoi c'était une erreur : TokenBundle est accédé depuis plusieurs threads simultanément (un par utilisateur actif). Omettre volatile sur des champs partagés muta est une erreur classique de concurrence Java que j'aurais dû anticiper dès la conception.

Temps perdu : ~20 min (revue + correction) | Sévérité : ⭐⭐⭐⭐


❌ Erreur S6-3 — Monitoring appelant des endpoints authentifiés

Ce que j'ai proposé : Lors de l'ajout des onglets Scripting dans le monitoring, j'ai référencé directement /api/admin/chaos/scripting/logs et /api/admin/chaos/scripting/status.

Ce qui s'est passé : Ces endpoints requièrent un cookie de session admin. Le monitoring server.js s'exécute sans session → 401 en boucle → onglet vide, logs pollués.

Pourquoi c'était une erreur : Le monitoring est un service sans état qui ne peut pas s'authentifier. J'aurais dû créer les endpoints /public/ simultanément avec les endpoints admin, et ne référencer que les publics depuis le monitoring.

Temps perdu : ~20 min | Sévérité : ⭐⭐⭐


❌ Erreur S6-4 — product.getStockQuantity() (champ inexistant)

Ce que j'ai proposé : Dans l'implémentation d'A3 et A8, le code accédait au stock via product.getStockQuantity().

Ce qui s'est passé : Le champ réel de l'entité Product est stockgetStockQuantity() n'existe pas → erreur de compilation.

Pourquoi c'était une erreur : J'aurais dû vérifier la définition de l'entité Product avant d'écrire le code qui y accède. Erreur classique d'hypothèse sur le nom de champ.

Temps perdu : ~10 min | Sévérité : ⭐⭐⭐


❌ Erreur S6-5 — Retry OrderService avec données déjà transformées

Ce que j'ai proposé : Une logique de retry sur createOrderFromItems() qui réutilisait les données de la première tentative.

Ce qui s'est passé : En cas d'exception sur la première tentative (ex. A8 race condition), la deuxième tentative recevait des prix déjà arrondis par A2 et un taux TVA déjà appliqué par A1 — cumul d'anomalies non intentionnel.

Pourquoi c'était une erreur : La logique de retry doit toujours repartir des données brutes originales. J'aurais dû identifier que les transformations A1 et A2 étaient effectuées in-place sur les arguments, rendant le retry non idempotent.

Temps perdu : ~25 min | Sévérité : ⭐⭐⭐⭐


📊 Récapitulatif Session 6

# Erreur Temps perdu Sévérité
S6-1 HMAC getBytes() sans charset (risque prod NAS) 0 (prévenu) ⭐⭐⭐⭐⭐
S6-2 TokenBundle sans volatile ~20 min ⭐⭐⭐⭐
S6-3 Monitoring → endpoints authentifiés (401 boucle) ~20 min ⭐⭐⭐
S6-4 getStockQuantity() (champ stock) ~10 min ⭐⭐⭐
S6-5 Retry avec données déjà transformées ~25 min ⭐⭐⭐⭐
TOTAL ~1h15

Pattern : Hypothèses sur l'état des données et sur l'environnement d'exécution — exactement le même pattern que les sessions précédentes, mais avec un ratio détection préventive (revue de code) vs détection réactive (bug en prod) nettement meilleur.