Aller au contenu

REX — PD-180 Webhooks

Story : PD-180 — Implémenter webhooks pour événements utilisateur Date : 2026-03-07


1. Résumé exécutif

Implémentation complète d'un module de webhooks sortants B2B avec HMAC-SHA256, BullMQ async, SSRF protection, et isolation multi-tenant RLS. La phase d'acceptabilité (étape 7) a été la plus productive : 2 BLOQUANTS sécurité détectés et corrigés (SSRF DNS rebinding, trigger append-only incomplet). Gate 8 GO en v1 avec un score de 8.75/10.

2. Points forts

  1. Architecture modulaire : 11 composants bien découpés (entities, DTOs, enums, services, controller, module, processor, listener, migration) — séparation des responsabilités claire.
  2. Pattern outbox robuste : Intention INSERT en transaction DB, enqueue BullMQ post-commit — zéro perte de message possible.
  3. Reviews LLM complémentaires : La review sécurité (7c) a détecté le DNS rebinding TOCTOU que ni ESLint, ni TSC, ni Sonar ne peuvent voir. L'investissement en 3 reviews parallèles (code/tests/sécurité) est justifié.
  4. Corrections immédiates : Les 2 BLOQUANTS et 5 MAJEURS corrigés avant Gate 8 — résultat GO en v1 sans boucle de correction.
  5. SSRF defense-in-depth : URL validation (HTTPS only) + DNS resolution + IP blocking (RFC 1918) + IP pinning (anti-rebinding) + maxRedirects:0.

3. Points d'amélioration

  1. Couverture de tests asymétrique : 36 tests couvrent 3 services sur 7 composants. Le delivery service (composant central, 600+ lignes) n'a aucun test direct. La phase de test (étape 2) devrait inclure des tests pour les composants d'orchestration, pas seulement les services de base.
  2. event_types en simple-array : Choix rapide (TEXT avec sérialisation par virgule) au lieu de PostgreSQL array natif. Filtrage en mémoire au lieu de SQL — fonctionnel mais crée une dette technique pour le filtrage à l'échelle.
  3. Rate limiter non-atomique : Les 2 pipelines Redis séparées créent une fenêtre de race. Un script Lua atomique serait préférable pour un service critique.
  4. RLS non applicable sur webhook_delivery_attempts : La table n'a pas de colonne org_id directe. L'isolation repose sur la FK vers webhook_deliveries (RLS active). Solution future : ajouter org_id redondant pour RLS directe.
  5. Gate 3 en 3 itérations : La spec initiale ChatGPT sous-estimait les invariants de sécurité réseau (SSRF, DNS rebinding). Piste : injecter une directive SSRF conditionnelle dans le prompt de spécification pour toute story avec requêtes HTTP sortantes.

4. Enseignements

  1. [Webhooks SSRF] IP pinning via https.Agent({ lookup }) obligatoire pour toute requête HTTP sortante — axios re-résout le DNS par défaut, ouvrant un TOCTOU DNS rebinding.
  2. [Webhooks Append-Only] Un trigger append-only doit couvrir BEFORE UPDATE OR DELETE — un BEFORE UPDATE seul laisse les DELETE ouverts. La purge technique utilise une fonction SECURITY DEFINER.
  3. [Webhooks Ping] Un ping doit cibler le webhook spécifique (delivery ciblée), pas broadcast via EventEmitter à tous les webhooks de l'org.
  4. [Webhooks Pattern] L'acceptabilité (étape 7) est la gate la plus efficace du workflow : 3 reviews LLM parallèles détectent des catégories d'écarts différentes (conformité, couverture, sécurité). Investissement rentable.
  5. [NestJS BullMQ] Le pattern @InjectQueue + @Processor avec retry managé manuellement (pas d'auto-retry BullMQ) donne un contrôle total sur la séquence de retry et l'état des tentatives.

5. Métriques

Métrique Valeur
Score Gate 3 GO (9.69/10)
Score Gate 5 GO (9.88/10)
Score Gate 8 GO (8.75/10)
Tests 36 (11 signature + 16 SSRF + 9 CRUD)
Fichiers source 21 (.ts)
Fichiers test 3 (.spec.ts)
BLOQUANTS corrigés 2
MAJEURS corrigés 5
Réserves documentées 4
Corrections post-confrontation 1 (trigger DELETE)