Auditeur : Claude (indépendant, mode factuel) Date : 2026-02-28 Documents audités : PD-277-specification.md (v2), PD-277-tests.md (v2) Domaine : crypto-proof (conformité formelle Prolog) Itération : 2
Corrections v1 → v2 vérifiées
| Constat v1 | Statut v2 | Détail |
| C1 (format nonce non défini) — Bloquant | Corrigé | §4 contractualise UUID v4 strict, 36 chars, lowercase ASCII, crypto.randomUUID(), rejet fail-closed hors format. |
| C2 (politique used_nonces non bornée) — Majeur | Corrigé | §6 : TTL aligné durée de vie LegalReKey, pas de purge séparée, destruction avec l'entité. |
| C3 (faits Prolog absents) — Bloquant | Corrigé | §8 liste les 3 faits canoniques entity_column exacts pour checks 23/24. |
| C4 (types SQL non contractualisés) — Majeur | Corrigé | §6 fournit DDL contractuel complet (JSONB, VARCHAR(255), NOT NULL, defaults). |
| C5 (atomicité non spécifiée) — Majeur | Corrigé | §7 contractualise transaction SERIALIZABLE ou advisory lock + rollback complet. |
| C6 (TC-INV non définis) — Bloquant | Corrigé | §5 des tests définit TC-INV-03, TC-INV-05, TC-INV-06, TC-INV-08 avec GIVEN/WHEN/THEN. |
| C7 (immuabilité certificats) — Mineur | Maintenu (cf. constat 7 ci-dessous) | Mécanisme d'enforcement toujours non précisé (domaine vs DB). |
| C8 (TC-NEG-05 sans spec) — Mineur | Corrigé | TC-NEG-05 revu, §10 liste PRE_CERTIFICATE_BINDING_FAILED couvrant certificats invalides/non vérifiables. |
| C9 (concurrence non traitée) — Majeur | Corrigé | §7 contractualise atomicité + sérialisation. ST-277-06 et TC-NEG-02 cohérents. |
| C10 (faits Prolog non vérifiables) — Bloquant | Corrigé | §8 fournit les faits canoniques. TC-NOM-03 vérifiable. |
| C11 (INV-277-06 couverture partielle) — Majeur | Amélioré | INV-277-06 explicitement "validé par configuration et preuves d'exploitation". TC-INV-06 défini. Tests §9 reconnaît non-testabilité unitaire. |
| C12 (origine nonce ambiguë) — Mineur | Partiellement corrigé (cf. constat 1 ci-dessous) | §4 dit "côté serveur uniquement", mais flux F2 dit "reçoit un nonce". |
| C13 (rotation certificat post-binding) — Mineur | Non traité (cf. constat 8 ci-dessous) | Hors périmètre explicite mais risque résiduel maintenu. |
Constats résiduels
Constat 1
Type : Ambiguïté
Référence : Spec §4 (Génération) vs §9 Flux F2 (étape 1)
Description : §4 stipule « Génération : crypto.randomUUID() côté serveur uniquement, jamais côté client ». Flux F2 stipule « Le système reçoit une demande reEncrypt contenant un nonce ». Si le nonce est reçu dans la requête, il est généré par l'appelant. Si l'appelant est un service backend interne, « côté serveur » est satisfait. Mais la spec ne définit pas qui est « le client » ni qui est « le serveur » dans ce contexte inter-services. Le mécanisme de génération (crypto.randomUUID) est-il une contrainte sur l'appelant ou sur le module legal-pre ?
Impact : Un implémenteur pourrait (a) générer le nonce dans legal-pre et le retourner au lieu de le recevoir, ou (b) accepter un nonce client-generated sans vérification d'origine. La testabilité de TC-ERR-01 (nonce absent) et TC-ERR-02 (nonce hors format) n'est pas affectée, mais le contrat d'API est ambigu sur la responsabilité de génération.
Gravité : Mineur
Constat 2
Type : Hypothèse dangereuse
Référence : Spec §6 DDL contractuel / §9 Stratégie migration DDL
Description : Le DDL contractuel impose owner_certificate_id VARCHAR(255) NOT NULL et recipient_certificate_id VARCHAR(255) NOT NULL sans valeur par défaut. La stratégie migration dit « Backfill : non requis si migration atomique et contraintes respectées ». Si des LegalReKey existants sont présents en base avant PD-277, ALTER TABLE ADD COLUMN ... NOT NULL échoue en PostgreSQL sans DEFAULT. L'hypothèse implicite est qu'aucun LegalReKey pré-existant n'est présent, mais cette hypothèse n'est pas listée dans §13 (Hypothèses explicites).
Impact : Si des données existent, la migration up échoue. TC-NOM-05 pourrait passer sur base vide mais échouer en intégration. L'hypothèse d'absence de données devrait être explicite (H-277-06) ou un DEFAULT transitoire contractualisé.
Gravité : Majeur
Constat 3
Type : Ambiguïté
Référence : Spec §6 — Structure des entrées used_nonces
Description : §6 définit used_nonces comme « array JSONB » avec DDL DEFAULT '[]'::jsonb. La structure de chaque entrée n'est pas contractualisée : s'agit-il de chaînes UUID brutes (["uuid1", "uuid2"]) ou d'objets avec métadonnées ({nonce: "uuid1", used_at: "...", request_id: "..."}) ? TC-NOM-02 vérifie « Trace persistée de N dans used_nonces » et TC-INV-03 vérifie « pas de duplication de N » — les assertions dépendent du format interne.
Impact : Deux implémentations conformes au DDL pourraient avoir des formats internes incompatibles, rendant les assertions de test non portables. Le mécanisme de vérification d'unicité (jsonb_array_elements, @>, opérateur SQL) dépend du format choisi.
Gravité : Mineur
Constat 4
Type : Incohérence Spec↔Tests
Référence : Tests §4 TC-ERR-10 — Référence CA-277-09
Description : TC-ERR-10 est intitulé « Contexte produisant un résultat < 24/24 » avec résultat « Verdict non conforme explicite / Livraison refusée ». Sa référence spec est « ERR-AUDIT-NONCOMPLIANT, CA-277-07, CA-277-09 ». CA-277-09 concerne la migration up/down sans erreur, pas la conformité audit. La référence correcte devrait être CA-277-07 uniquement. L'inclusion de CA-277-09 est erronée.
Impact : Traçabilité incorrecte dans la matrice de couverture. Mineur car CA-277-07 est bien référencé et le test est fonctionnellement correct.
Gravité : Mineur
Constat 5
Type : Ambiguïté
Référence : Spec §10 — ERR-PROLOG-FACTS-OUTDATED / Tests §4 TC-ERR-09
Description : §10 liste ERR-PROLOG-FACTS-OUTDATED comme cas d'erreur au même niveau que les erreurs runtime API (ERR-NONCE-MISSING, PRE_NONCE_REPLAY_DETECTED, etc.). Or, dans le domaine crypto-proof, la détection de faits obsolètes est un contrôle de build/CI (régénération extract-facts.py), pas un code d'erreur runtime. TC-ERR-09 teste « Faits Prolog non régénérés ou incohérents → audit invalide » mais ne précise pas le mécanisme de détection de l'incohérence (checksum, timestamp, diff automatique).
Impact : Un implémenteur pourrait tenter d'implémenter une détection runtime de la cohérence des faits Prolog (complexité inutile) au lieu de s'appuyer sur le pipeline CI/CD. Le test TC-ERR-09 est non déterministe sans mécanisme de détection explicite.
Gravité : Mineur
Constat 6
Type : Hypothèse dangereuse
Référence : Spec §7 — Atomicité et concurrence / SERIALIZABLE
Description : §7 offre le choix entre isolation SERIALIZABLE et advisory lock. En PostgreSQL, SERIALIZABLE peut produire des erreurs de sérialisation (SQLSTATE 40001) nécessitant un retry côté applicatif. La spec ne distingue pas le comportement attendu entre : (a) erreur de sérialisation transitoire (retry possible) et (b) rejet logique fail-closed (pas de retry). ERR-PERSISTENCE-CONTROL (§10) pourrait couvrir le cas (a), mais la sémantique retry vs rejet-définitif n'est pas contractualisée.
Impact : Si l'implémentation choisit SERIALIZABLE, le client devra distinguer les erreurs retryable des rejets définitifs. Le contrat d'API ne le spécifie pas. TC-NEG-02 (rejeu concurrent) attend « au plus 1 succès » ce qui est satisfait dans les deux cas, mais l'expérience appelant diffère (erreur transitoire vs rejet définitif).
Gravité : Mineur
Constat 7
Type : Ambiguïté
Référence : Spec §6 Immuabilité certificats / INV-277-05
Description : §6 stipule « Toute tentative de modification ultérieure est rejetée en fail-closed ». TC-ERR-08 vérifie le rejet via « tentative de modification owner_certificate_id ou recipient_certificate_id ». Mais le mécanisme d'enforcement n'est pas contractualisé : (a) guard applicatif dans le service (reject sur DTO), (b) trigger/constraint PostgreSQL (BEFORE UPDATE), (c) propriété read-only TypeORM. Le test TC-ERR-08 couvre uniquement le chemin API métier (a). Un accès direct DB contournerait (a) sans être couvert.
Impact : INV-277-05 n'est garanti que si l'enforcement est au moins applicatif. Pour un invariant « non négociable », le niveau de protection (applicatif seul ou DB aussi) devrait être contractualisé. Risque résiduel faible en contexte crypto-proof (accès DB restreint).
Gravité : Mineur
Constat 8
Type : Risque sécu/conformité
Référence : Spec §5 INV-277-04, INV-277-05 / Aucun test associé
Description : Le cycle de vie post-binding des certificats n'est pas traité. Si un certificat PKI (owner ou recipient) est révoqué ou expire après la création du LegalReKey, le binding existant pointe vers un certificat invalide. La spec contractualise la validation à la création (INV-277-04) et l'immuabilité post-création (INV-277-05), mais ne traite pas l'invalidation postérieure. Aucun cas d'erreur, aucun scénario de test, et aucune hypothèse explicite ne couvrent ce cas.
Impact : Un LegalReKey pourrait rester actif avec des certificats révoqués, ce qui est un risque de conformité PKI. Cependant, le périmètre PD-277 est explicitement limité au module legal-pre et à la liaison au moment de la création. La gestion du cycle de vie des certificats pourrait relever d'une story séparée.
Gravité : Mineur
Constat 9
Type : Incohérence Spec↔Tests
Référence : Spec §12 (ST-277-XX) vs Tests §3-§7 (TC-XX)
Description : La spec §12 définit 8 scénarios de test (ST-277-01 à ST-277-08). Le document de tests utilise une nomenclature différente (TC-NOM-XX, TC-ERR-XX, TC-INV-XX, TC-NR-XX, TC-NEG-XX). Il n'existe aucune table de correspondance explicite entre les ST-277-XX de la spec et les TC-XX des tests. Exemples : ST-277-01 ≈ TC-NOM-01, ST-277-04 ≈ TC-NOM-02, ST-277-06 ≈ TC-NEG-02, mais ces mappings sont implicites.
Impact : Traçabilité spec→tests rompue pour un auditeur externe. Un audit formel exigerait de reconstituer manuellement la correspondance. Les ST-277-XX pourraient être considérés comme non couverts par les tests si la correspondance n'est pas formalisée.
Gravité : Mineur
Constat 10
Type : Ambiguïté
Référence : Spec §4 (Validation) / INV-277-01-fail-closed
Description : §4 stipule « tout nonce hors format contractuel est rejeté en fail-closed avant traitement crypto » et définit le format comme UUID v4 strict (xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx). La convention UUID v4 impose que y ∈ {8, 9, a, b} (variant bits RFC 4122). La spec ne précise pas si la validation inclut la vérification des variant bits (position 15 = 4, position 20 ∈ {8,9,a,b}) ou se limite à la forme regex 36 caractères lowercase. crypto.randomUUID() produit des UUID conformes, mais un nonce forgé avec y=0 serait-il rejeté ?
Impact : Différence mineure de surface de validation. TC-NEG-01 (« variations de casse/espaces/normalisation ») ne couvre pas explicitement les variant bits invalides. En pratique, si le serveur génère via crypto.randomUUID(), ce cas ne se présente pas dans le flux normal, mais un appelant malveillant pourrait soumettre un UUID structurellement valide (36 chars, lowercase) mais non conforme RFC 4122.
Gravité : Mineur
Synthèse
| Gravité | Nombre |
| Bloquant | 0 |
| Majeur | 1 |
| Mineur | 9 |
| Total | 10 |
Évolution v1 → v2
| Métrique | v1 | v2 | Delta |
| Bloquants | 3 | 0 | -3 |
| Majeurs | 5 | 1 | -4 |
| Mineurs | 5 | 9 | +4 |
| Total | 13 | 10 | -3 |
Tendance : Convergence forte. Les 3 bloquants v1 sont tous corrigés. Les 5 majeurs v1 sont réduits à 1 résiduel (migration NOT NULL sans hypothèse explicite). Les nouveaux mineurs sont des points de précision contractuelle, pas des risques fonctionnels.
Constat majeur résiduel
- Migration NOT NULL sans hypothèse d'absence de données (Constat 2) — L'ajout de colonnes NOT NULL sans DEFAULT sur
owner_certificate_id / recipient_certificate_id échoue si des LegalReKey pré-existants sont en base. L'hypothèse d'absence de données n'est pas formalisée dans §13.
Points d'attention (mineurs, non bloquants)
- Origine du nonce (appelant vs module) pourrait être clarifié par une note architecturale (Constat 1).
- Structure interne des entrées used_nonces (string vs objet) mérite une ligne dans le DDL contractuel (Constat 3).
- La distinction erreurs runtime vs erreurs build/CI dans §10 éviterait toute confusion d'implémentation (Constat 5).
- La correspondance ST-277-XX ↔ TC-XX devrait être formalisée dans une table de mapping (Constat 9).