Aller au contenu

PD-253 — Specification Review (Gate 3, v1)

Auditeur : Claude (indépendant) Date : 2026-03-12 Documents audités : PD-253-specification.md, PD-253-tests.md Learnings injectés : PD-244, PD-278, PD-85, PD-283


Écarts identifiés

E-01 — État FAILED_TIMEOUT absent de la machine à états

Type : Contradiction
Référence : Spec §5.4 (machine à états) vs §6 (504 EXPORT_TIMEOUT) + Tests TC-ERR-09
Description : La section §6 mentionne l'état `FAILED_TIMEOUT` comme état final du timeout (504 EXPORT_TIMEOUT).
Le test TC-ERR-09 vérifie explicitement l'état `FAILED_TIMEOUT`. Or la machine à états §5.4 ne liste
que `FAILED` parmi les états terminaux — `FAILED_TIMEOUT` n'apparaît ni dans la liste des états,
ni dans les transitions autorisées.
Impact : Implémentation ambiguë — `FAILED_TIMEOUT` est-il un état distinct (avec enum dédié)
ou un sous-cas de `FAILED` ? Si état distinct, la machine à états est incomplète.
Si sous-cas, le test TC-ERR-09 vérifie un état inexistant.
Gravité : Bloquant

E-02 — Confirmation de téléchargement non définie

Type : Ambiguïté
Référence : Spec §5.5 étape 3 — "Passage DOWNLOADED à confirmation de téléchargement réussi"
Description : Le mécanisme de confirmation du téléchargement n'est pas contractualisé.
Pour un fichier de 100 GB, la distinction entre "requête HTTP initiée" et "téléchargement complet"
est significative. Aucun critère observable n'est défini pour déclencher la transition
READY_FOR_DOWNLOAD -> DOWNLOADED.
Impact : L'implémenteur doit choisir arbitrairement (callback client, fin de stream S3,
polling explicite) sans contrat.
Gravité : Majeur

E-03 — ProofEnvelope : champs obligatoires vs documents à ancrage pending

Type : Ambiguïté
Référence : Spec §2 Inclus + INV-253-02-proof-complete + INV-253-08-pending-anchor
Description : INV-253-02 exige une "ProofEnvelope complète conforme aux contrats amont" pour chaque
document inclus. La section §2 (Inclus) liste : plaintext_hash, ciphertext_hash, preuve Merkle,
TSA RFC3161, signature serveur, ancrage blockchain. Or INV-253-08 stipule que les documents à
ancrage `pending` sont inclus. Un document `pending` n'a pas d'ancrage blockchain finalisé
(pas de tx_id confirmé). INV-253-02 et INV-253-08 sont potentiellement contradictoires si
"complète" signifie "tous les champs renseignés".
Impact : L'implémenteur ne sait pas si un document `pending` avec ProofEnvelope partielle
(sans ancrage) est conforme à INV-253-02 ou doit déclencher un 422.
Gravité : Majeur

E-04 — Erreur 422 synchrone vs assemblage asynchrone

Type : Contradiction
Référence : Spec §6 (422 PROOF_ENVELOPE_INCOMPLETE) vs §5.5 étape 2 (assemblage asynchrone)
+ Tests TC-ERR-06
Description : Le code 422 est un code HTTP synchrone retourné au client. Or la vérification de
complétude des ProofEnvelopes se produit pendant l'assemblage asynchrone (§5.5 étape 2, worker).
Le client a déjà reçu un 2xx (ou 409) lors de la création. Le test TC-ERR-06 reconnaît
l'ambiguïté ("ou FAILED selon point d'échec contractuel") mais la spec ne tranche pas.
Impact : Si la vérification est async, le client ne recevra jamais un 422 — l'export passera
en FAILED. Le contrat d'erreur est incohérent avec le flux.
Gravité : Majeur

E-05 — Annulation utilisateur non testée

Type : Incohérence Spec↔Tests
Référence : Spec §5.4 transitions REQUESTED->CANCELLED et ASSEMBLING->CANCELLED
+ Tests (aucun test dédié)
Description : La machine à états autorise explicitement les transitions REQUESTED->CANCELLED et
ASSEMBLING->CANCELLED. Aucun test nominal ni test d'erreur ne couvre l'annulation par
l'utilisateur. TC-NOM-09 vérifie les transitions "autorisées puis interdites" de manière
générique, mais aucun Given/When/Then ne cible spécifiquement le flux d'annulation.
Impact : Le comportement d'annulation (endpoint API, effets sur le worker, nettoyage package
partiel, événement audit) n'est ni contractualisé ni vérifié.
Gravité : Majeur

E-06 — Fenêtre de rétention temporaire non quantifiée

Type : Ambiguïté
Référence : INV-253-12-no-residuals — "fenêtre de rétention contractualisée"
Description : INV-253-12 interdit les résidus "au-delà de la fenêtre de rétention contractualisée".
Cette fenêtre n'est définie nulle part. §5.2 contractualise `package_retention_ttl` (7j par défaut)
pour le package final prêt, mais pas pour les fichiers temporaires pendant l'assemblage.
Les artefacts intermédiaires (fragments, clés éphémères, package partiel) n'ont pas de TTL.
Impact : TC-INV-12 est non vérifiable en l'absence de valeur contractuelle pour cette fenêtre.
Gravité : Majeur

E-07 — Contrôle d'accès sur endpoint de statut non contractualisé

Type : Risque sécu/conformité
Référence : Spec §5.5 étape 3 + §5.7 (scope de contrôle)
Description : La spec contractualise le contrôle d'accès sur la création d'export (403 FORBIDDEN_SCOPE)
mais ne mentionne pas de contrôle d'accès sur les endpoints de consultation de statut
(GET export status) ni de téléchargement (URL signée). Un UUID v4 n'est pas un mécanisme
de sécurité — si l'endpoint de statut n'est pas protégé, un attaquant connaissant l'export_id
pourrait obtenir l'URL signée de téléchargement.
Impact : Fuite potentielle de données archivées d'un utilisateur via énumération ou
interception d'export_id.
Gravité : Majeur

E-08 — Scopes non-GLOBAL sans test nominal dédié

Type : Incohérence Spec↔Tests
Référence : Spec §5.5 (4 granularités GLOBAL/VAULT/PERIOD/SELECTION) + Tests nominaux
Description : Seul le scope `GLOBAL` est couvert par un test nominal détaillé (TC-NOM-01).
Les scopes `VAULT`, `PERIOD` et `SELECTION` ne sont mentionnés que dans TC-NR-01
(non-régression générique). Les filtres associés (`vault_id`, `from/to`, `document_ids`)
et leurs règles de sélection ne sont vérifiés par aucun scénario Given/When/Then.
Impact : Les règles de filtrage par vault, période et sélection pourraient être
implémentées de manière incohérente sans être détectées.
Gravité : Mineur

E-09 — URL signée : partageabilité et binding utilisateur

Type : Risque sécu/conformité
Référence : Spec §5.5 étape 3 + §5.2 (TTL URL signée)
Description : La spec ne précise pas si l'URL signée est liée à l'utilisateur authentifié
(bearer token, IP, session) ou si elle est auto-portante (tout possesseur peut télécharger).
Pour un export pouvant contenir des documents RGPD, une URL auto-portante partageable
constitue un risque de fuite de données personnelles.
Impact : Non-conformité potentielle RGPD si l'URL peut être transmise à un tiers.
Gravité : Mineur

E-10 — destruction-log.json : hash de destruction sans vérification fonctionnelle

Type : Hypothèse dangereuse
Référence : Spec §5.1 (destruction_act_hash) + INV-253-09 + Tests TC-NOM-08
Description : Le `destruction_act_hash` est validé en format (regex SHA3-256) par §5.1,
et TC-NOM-08 vérifie sa "validité". Or la spec ne contractualise pas la vérification
fonctionnelle de ce hash — il n'est jamais comparé/recalculé contre l'acte de destruction
source (PD-250). Le learning PD-283 rappelle que la validation format ≠ validation fonctionnelle
pour les champs de sécurité.
Impact : Un `destruction_act_hash` pourrait être syntaxiquement valide mais référencer un acte
inexistant, sans que l'export ne le détecte.
Gravité : Mineur

E-11 — Race condition sur quota sans mécanisme de verrouillage

Type : Hypothèse dangereuse
Référence : INV-253-07-quota-concurrence + Tests TC-NEG-05
Description : TC-NEG-05 vérifie qu'une seule création est acceptée parmi des requêtes
simultanées. Cependant, la spec ne contractualise aucun mécanisme de sérialisation
(SELECT FOR UPDATE, UNIQUE constraint, advisory lock). Le test suppose un résultat
déterministe sans contrainte d'implémentation.
Impact : Sans contrainte de sérialisation, deux requêtes concurrentes pourraient toutes
deux passer la vérification de quota et créer deux exports.
Gravité : Mineur

E-12 — READY_FOR_DOWNLOAD bloquant sans sortie volontaire

Type : Ambiguïté
Référence : Spec §3 (définition "Export actif") + §5.4 (transitions READY_FOR_DOWNLOAD)
Description : Un export `READY_FOR_DOWNLOAD` est compté comme "actif" (quota). Or cet état
n'a ni transition vers `CANCELLED` ni vers un autre état volontaire — seuls `DOWNLOADED`
(téléchargement confirmé) et `EXPIRED` (attente TTL) sont autorisés. Un utilisateur avec
un export prêt qu'il ne souhaite plus télécharger est bloqué jusqu'à expiration (7j par défaut).
Impact : Blocage fonctionnel potentiel de l'utilisateur pendant la durée du TTL de rétention.
Gravité : Mineur

Synthèse

Gravité Nombre IDs
Bloquant 1 E-01
Majeur 6 E-02, E-03, E-04, E-05, E-06, E-07
Mineur 5 E-08, E-09, E-10, E-11, E-12
Total 12

Points forts

  • Machine à états exhaustive avec transitions explicites (modulo E-01).
  • Dual manifest BagIt pour intégrité vérifiable offline.
  • Modèle de données contractuel avec formats/regex précis.
  • Atomicité multi-composant bien séparée (sync/async).
  • Audit fail-closed conforme au pattern anti-catch-absorb (learning PD-85).

Risques principaux

  1. Cohérence de la machine à états (E-01) : FAILED_TIMEOUT non défini → bloquant.
  2. Flux async vs codes HTTP (E-04) : le contrat d'erreur 422 est incompatible avec l'assemblage asynchrone.
  3. Sécurité d'accès aux exports (E-07) : endpoint statut/téléchargement non protégé.