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¶
- Architecture modulaire : 11 composants bien découpés (entities, DTOs, enums, services, controller, module, processor, listener, migration) — séparation des responsabilités claire.
- Pattern outbox robuste : Intention INSERT en transaction DB, enqueue BullMQ post-commit — zéro perte de message possible.
- 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é.
- Corrections immédiates : Les 2 BLOQUANTS et 5 MAJEURS corrigés avant Gate 8 — résultat GO en v1 sans boucle de correction.
- 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¶
- 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.
event_typesen 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.- 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.
- RLS non applicable sur
webhook_delivery_attempts: La table n'a pas de colonneorg_iddirecte. L'isolation repose sur la FK verswebhook_deliveries(RLS active). Solution future : ajouterorg_idredondant pour RLS directe. - 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¶
- [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. - [Webhooks Append-Only] Un trigger append-only doit couvrir
BEFORE UPDATE OR DELETE— unBEFORE UPDATEseul laisse les DELETE ouverts. La purge technique utilise une fonctionSECURITY DEFINER. - [Webhooks Ping] Un ping doit cibler le webhook spécifique (delivery ciblée), pas broadcast via EventEmitter à tous les webhooks de l'org.
- [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.
- [NestJS BullMQ] Le pattern
@InjectQueue+@Processoravec 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) |