PD-80 — Specification Review¶
Auditeur : ChatGPT (Gate 3 — CONFORMITY_CHECK) Documents audités : PD-80-specification.md (v3), PD-80-tests.md (v3) Date : 2026-03-12
Constats¶
C-01 — Ambiguïté : relation entre les trois rate-limits¶
Type : Ambiguïté
Référence : §5.2 (rate_limit_urgent=1/h, rate_limit_minor_hour=2/h, rate_limit_enterprise_hour=10/h)
Description : La spec définit trois rate-limits distincts sans préciser leur relation :
- rate_limit_urgent (1 req/h/user) — semble être le rate-limit par défaut
- rate_limit_minor_hour (2 req/h/compte)
- rate_limit_enterprise_hour (10 req/h/user)
Quel rate-limit s'applique à un compte premium ? rate_limit_urgent (1/h) ?
Pour un mineur, rate_limit_urgent (1/h) et rate_limit_minor_hour (2/h) coexistent-ils ?
Le "par user" vs "par compte" ajoute à la confusion (un compte mineur peut avoir un tuteur).
Impact : L'implémentation peut appliquer un rate-limit différent de celui attendu, ou appliquer
les deux simultanément de manière non déterministe.
Gravité : Bloquant
C-02 — Ambiguïté : quota applicable aux comptes mineurs¶
Type : Ambiguïté
Référence : §5.1 (note enum account_type), §5.2 (quotas par plan), INV-80-02
Description : Les quotas mensuels sont définis par plan (freemium=3, premium=10, enterprise=1000).
Le champ account_type distingue minor|standard|premium|enterprise. Le champ plan distingue
freemium|premium|enterprise. Quel plan a un compte mineur (account_type=minor) ?
La spec ne définit pas le mapping account_type=minor → plan. Si minor a plan=freemium,
le quota est 3/mois. Si minor a un plan dédié, le quota est indéfini.
INV-80-02 dit que le fast-track est automatique pour les mineurs, mais INV-80-07 impose
les quotas "avant admission en queue prioritaire". Un mineur freemium à 3/3 est-il rejeté 403 ?
Impact : Un mineur pourrait se voir refuser un scellement urgent alors que la protection des mineurs
est l'objectif premier de la story. Ou inversement, un quota non défini pourrait permettre un
abus illimité.
Gravité : Bloquant
C-03 — Ambiguïté : définition de "job BullMQ actif" pour la réconciliation¶
Type : Ambiguïté
Référence : §5.10 (critère de détection d'orphelin : "sans job BullMQ actif correspondant")
Description : BullMQ distingue plusieurs états de job : waiting, delayed, active, completed, failed.
La spec ne définit pas quels états BullMQ comptent comme "actif" pour le critère d'orphelin.
Un job en état "delayed" (retry planifié) a un job BullMQ présent mais pas en cours
d'exécution. Le considérer comme "actif" empêcherait la réconciliation d'un job réellement
bloqué. Le considérer comme "inactif" déclencherait une fausse réconciliation.
Impact : Faux positifs (double exécution) ou faux négatifs (orphelins non détectés).
Gravité : Majeur
C-04 — Ambiguïté : conditions de déclenchement des transitions retour¶
Type : Ambiguïté
Référence : §5.4 (TSA_PENDING → QUEUED_PRIORITY, TSA_SEALED → TSA_PENDING)
Description : Les transitions retour sont décrites comme "décision technique explicite" avec
des exemples entre parenthèses ("invalidation du résultat TSA", "token TSA corrompu").
Les conditions exactes de déclenchement ne sont pas listées exhaustivement. L'implémentation
devra décider quels cas justifient un retour vs un retry dans le même état. La frontière
entre "retry (même état)" et "requeue (transition retour)" n'est pas contractualisée.
Impact : Comportement divergent entre implémentations. Impossible de tester exhaustivement.
Gravité : Majeur
C-05 — Contradiction potentielle : cycle non borné via transitions retour¶
Type : Hypothèse dangereuse
Référence : §5.4 (transitions retour), INV-80-06 (retries)
Description : La machine d'états autorise le cycle :
TSA_PENDING → QUEUED_PRIORITY → TSA_PENDING → QUEUED_PRIORITY → ...
et : TSA_SEALED → TSA_PENDING → QUEUED_PRIORITY → TSA_PENDING → ...
INV-80-06 borne les retries à 4 (5e échec → arrêt), mais §5.4 point 2 dit explicitement
que "les transitions retour sont des requeue internes distincts des retries". Le compteur
de retry est "préservé" (TC-NOM-16) mais pas incrémenté par la transition retour elle-même.
Seul le final_timeout borne le cycle, mais aucune borne explicite sur le nombre de
transitions retour n'est contractualisée. Si les transitions retour sont fréquentes,
le job peut osciller indéfiniment jusqu'au timeout.
Impact : Risque d'oscillation sans progrès. Pas de distinction entre "retry productif" et
"boucle stérile" avant le final_timeout.
Gravité : Majeur
C-06 — Incohérence Spec↔Tests : FAILED_TIMEOUT terminal non testé explicitement¶
Type : Incohérence Spec↔Tests
Référence : §5.4 (FAILED_TIMEOUT → * : INTERDITE), CA-14, TC-ERR-09, TC-INV-01
Description : CA-14 dit "États terminaux bloquent toute sortie" (pluriel). TC-ERR-09 ne teste
que SEALED. TC-INV-01 est défini comme "TC-ERR-09 + TC-ERR-10", mais TC-ERR-10 teste
les états non terminaux avec transitions non listées. Aucun test ne vérifie explicitement
que FAILED_TIMEOUT rejette une transition sortante.
La matrice §5 note INV-80-01 couvert par TC-INV-01, mais le cas FAILED_TIMEOUT n'y est pas.
Impact : Régression possible si l'implémentation oublie de bloquer les transitions depuis
FAILED_TIMEOUT.
Gravité : Majeur
C-07 — Incohérence Spec↔Tests : destruction DEK sur transition terminale non testée¶
Type : Incohérence Spec↔Tests
Référence : §5.14 (destruction DEK post-scellement), TC-NOM-09/TC-INV-09
Description : §5.14 contractualise la suppression de la DEK wrappée "dans la même transaction
que la transition terminale" (SEALED ou FAILED_TIMEOUT). TC-NOM-09 vérifie uniquement
l'absence de secrets en clair en base (INV-80-09). Aucun test ne vérifie :
(a) que la DEK wrappée est effectivement supprimée après SEALED,
(b) que la DEK wrappée est supprimée après FAILED_TIMEOUT,
(c) que la suppression est synchrone dans la transaction terminale.
Impact : DEK wrappées résiduelles en base après scellement, accumulation de matériel crypto
inutile, risque de non-conformité rétention.
Gravité : Majeur
C-08 — Incohérence Spec↔Tests : scénario INSUFFICIENT_DATA sans test dédié¶
Type : Incohérence Spec↔Tests
Référence : §3 (P95, N<100 → INSUFFICIENT_DATA), §5.2 (p95_min_samples), TC-NOM-03
Description : La spec contractualise un comportement spécifique quand N < p95_min_samples :
"P95 marqué INSUFFICIENT_DATA, aucune violation SLA émise". Ce comportement est mentionné
dans une clause AND de TC-NOM-03 mais n'a pas de test dédié. TC-NOM-03 GIVEN exige N≥100.
Le cas inverse (N<100) n'est jamais le scénario principal d'un test.
Impact : Le comportement INSUFFICIENT_DATA pourrait ne pas être implémenté ou mal implémenté
sans régression visible.
Gravité : Mineur
C-09 — Hypothèse dangereuse : panne Redis corrélée queue + réconciliation¶
Type : Hypothèse dangereuse
Référence : §5.10 (lock Redis), §5.7 (queue BullMQ/Redis)
Description : Le worker de réconciliation utilise un lock distribué Redis pour éviter la double
exécution. BullMQ utilise le même Redis pour la queue. En cas d'indisponibilité Redis,
les deux mécanismes échouent simultanément : plus de queue ET plus de réconciliation.
La spec ne mentionne pas cette dépendance corrélée ni le comportement attendu en cas
de panne Redis totale.
Impact : Perte simultanée du mécanisme principal (queue) et du filet de sécurité (réconciliation).
Les jobs en cours n'ont plus aucune garantie de progression.
Gravité : Majeur
C-10 — Hypothèse dangereuse : vérification NTP au démarrage uniquement¶
Type : Hypothèse dangereuse
Référence : §5.13 (synchronisation horloge)
Description : La vérification NTP ±1s est effectuée "au démarrage du service". Aucune vérification
périodique en runtime n'est spécifiée. Une dérive d'horloge survenant après le démarrage
(perte de synchronisation NTP, VM migration) n'est pas détectée.
Les calculs de latence P95, les SLA par étape et les horodatages TSA dépendent de cette
synchronisation.
Impact : Mesures SLA faussées et horodatages TSA potentiellement non fiables sans détection.
Gravité : Mineur
C-11 — Risque sécurité : cycle de vie du token TSA invalidé¶
Type : Risque sécu/conformité
Référence : §5.4 (transition retour TSA_SEALED → TSA_PENDING), TC-NOM-17
Description : TC-NOM-17 dit "l'horodatage TSA précédent est invalidé (nouveau TSA requis)".
La spec ne définit pas comment l'ancien token TSA est traité :
- Est-il supprimé de la base ?
- Est-il marqué comme invalidé avec motif ?
- Reste-t-il en base avec un statut ambigu ?
Un token TSA RFC 3161 est un artefact probatoire horodaté. Son cycle de vie post-invalidation
a des implications de conformité (traçabilité, non-répudiation, audit).
Impact : Token TSA orphelin en base pouvant être confondu avec un token valide lors d'un audit.
Risque de non-conformité traçabilité.
Gravité : Majeur
C-12 — Risque sécurité : vecteur d'abus compte mineur non borné en volume mensuel¶
Type : Risque sécu/conformité
Référence : INV-80-02, §5.2 (rate_limit_minor_hour=2/h), C-02 (quota mineur indéfini)
Description : Un compte mineur avec fast-track automatique (INV-80-02) et rate-limit 2/h peut
générer jusqu'à 48 scellements urgents/jour soit ~1440/mois. Si le quota mensuel n'est
pas défini pour les mineurs (cf. C-02), aucune borne mensuelle ne s'applique.
En cas de compromission du compte, l'attaquant bénéficie du fast-track automatique sans
aucune action manuelle requise et sans plafond mensuel contractualisé.
Le rate_limit_minor_hour + "alerte sécurité" atténue le risque horaire mais pas le volume
cumulé mensuel.
Impact : Abus de ressources prioritaires (TSA, queue, blockchain) via un compte mineur compromis.
Gravité : Majeur
C-13 — Ambiguïté : comportement en cas d'échec total de notification¶
Type : Ambiguïté
Référence : INV-80-08, §5.11, §5.12
Description : INV-80-08 est "satisfait si au moins un canal de notification réussit". La spec
ne définit pas le comportement si TOUS les canaux échouent :
- Push échoue (ou pas de device → fallback email)
- Email échoue
- Webhook échoue (retries épuisés → webhook_delivery_failed journalisé)
Dans ce cas, INV-80-08 est violé. Quelle est la conséquence ? Le scellement reste SEALED
(état terminal, pas de rollback possible). L'utilisateur n'est jamais informé.
Aucune escalade spécifique à l'échec total de notification n'est prévue.
Impact : Scellement réussi mais utilisateur jamais notifié. Violation d'INV-80-08 sans
remédiation contractualisée.
Gravité : Mineur
C-14 — Non testable : alerte CRITICAL pour reconciliation_max_catchup_delay dépassé¶
Type : Non testable
Référence : §5.2 (reconciliation_max_catchup_delay : "alerte CRITICAL si dépassé")
Description : §5.2 définit que le dépassement de reconciliation_max_catchup_delay (30 min)
déclenche une alerte CRITICAL. Aucun test (TC-NOM-11, TC-NOM-18) ne vérifie ce cas.
TC-NOM-11 vérifie le rattrapage SOUS le délai (happy path). Le cas où le rattrapage
dépasse 30 min n'est pas couvert.
Impact : L'alerte CRITICAL pourrait ne jamais être implémentée.
Gravité : Mineur
C-15 — Ambiguïté : éligibilité au fast-track manuel (§5.6)¶
Type : Ambiguïté
Référence : §1 ("utilisateurs autorisés"), §5.6, INV-80-03, TC-NOM-02
Description : §1 mentionne "utilisateurs autorisés (manuel)" mais ne liste pas les critères
d'autorisation au-delà du quota/rate-limit. TC-NOM-02 GIVEN dit "Compte
standard/premium/enterprise éligible" — un compte minor est-il éligible au fast-track
manuel EN PLUS de l'automatique ? Si oui, le mineur peut cumuler auto + manuel.
Si non, la restriction n'est pas contractualisée.
Impact : Comportement API ambigu pour les requêtes manuelles urgentes venant d'un compte mineur.
Gravité : Mineur
Synthèse¶
| Gravité | Nombre | IDs |
|---|---|---|
| Bloquant | 2 | C-01, C-02 |
| Majeur | 8 | C-03, C-04, C-05, C-06, C-07, C-09, C-11, C-12 |
| Mineur | 5 | C-08, C-10, C-13, C-14, C-15 |
| Total | 15 |