PD-177 - CONFORMITY_CHECK - Gate 3¶
Date de revue : 2026-02-23 Documents revus : PD-177-specification.md, PD-177-tests.md Contexte : Audit technique independant, orientation contractualisation / testabilite / conformite Codebase de reference : ProbatioVault-backend (commit courant)
Synthese¶
| Gravite | Nombre |
|---|---|
| Bloquant | 4 |
| Majeur | 8 |
| Mineur | 5 |
| Total | 17 |
Ecarts identifies¶
ECART-01¶
Type : Ambiguite Reference : Spec §4 INV-177-13 — champ "horodatage d'emission" Description : INV-177-13 exige la journalisation de l'"horodatage d'emission" sans specifier : - le referentiel temporel (UTC ? timezone applicative ?), - la precision requise (seconde, milliseconde, microseconde), - le format (ISO 8601 ? epoch ?). Le learning contextuel [Blockchain Merkle Anchor] indique explicitement que le format ISO 8601 UTC ms doit etre defini des la specification pour eviter des iterations de gate. Impact : Deux implementations differentes pourraient produire des horodatages incomparables, rendant l'audit probatoire indeterministe. Gravite : Majeur
ECART-02¶
Type : Ambiguite Reference : Spec §4 INV-177-16 — "confirmation reseau positive ou statut d'abandon explicite conforme aux codes d'erreur" Description : La notion de "confirmation reseau positive" n'est pas definie formellement. La spec mentionne une "politique de confirmations applicable" (HYP-177-06) sans la formaliser. Dans le code existant, il y a une contradiction factuelle : CONFIRMATION_BLOCKS = 12 (hardcode dans le processor, ligne 66) vs ANCHOR_CONFIRMATION_BLOCKS = 30 (dans anchor.constants.ts) vs les valeurs configurees par reseau (Polygon: 5, Arbitrum: 1 dans blockchain.config.ts). La spec ne precise pas quelle valeur fait foi pour PD-177. Impact : Un test TC-177-05 ("confirmation reseau positive dans la fenetre configuree") ne peut pas etre ecrit de facon deterministe sans savoir quel seuil de confirmations valide la finalisation. Gravite : Bloquant
ECART-03¶
Type : Ambiguite Reference : Spec §4 INV-177-04 — enum ethereum_l2 | tezos Description : L'invariant specifie que le champ blockchain doit "appartenir a l'enum ethereum_l2 | tezos" mais ajoute ensuite "pour PD-177, seules les preuves ethereum_l2 sont produites". La spec ne precise pas le comportement attendu si une preuve tezos est recue en entree (verification tierce FN-177-03). Doit-elle etre rejetee ? Acceptee en lecture seule ? Impact : Le test TC-177-07 valide uniquement ethereum_l2 en sortie mais ne couvre pas la reaction du systeme face a une valeur tezos en entree de verification. Gravite : Mineur
ECART-04¶
Type : Contradiction Reference : Spec §4 INV-177-14 vs AnchorBatch entity existante Description : INV-177-14 exige que "chaque transaction d'ancrage soit reliee de maniere deterministe a une entree du registre append-only". Or dans le code existant, l'entite AnchorBatch (schema vault_blockchain) est distincte du registre append-only Merkle (schema vault_merkle). La liaison se fait via le champ treeId (nullable) de AnchorBatch. La spec ne precise pas si le "registre append-only" designe le schema vault_merkle, le schema vault_blockchain, ou les deux. L'entite AnchorBatch elle-meme possede un champ errorReason mutable et un statut modifiable — elle n'est donc pas append-only au sens strict pour tous ses champs (seule l'immutabilite post-FINALIZED est garantie par trigger DB). Impact : L'implementation pourrait satisfaire le test TC-177-06 sur un registre mais pas l'autre, creant une ambiguite contractuelle sur le perimetre de l'immutabilite. Gravite : Majeur
ECART-05¶
Type : Incoh&erence Spec<->Tests Reference : Spec §4 INV-177-13 (champs minimaux) vs TC-177-06 (contenu minimal append-only) Description : INV-177-13 exige : "identifiant d'ancrage interne, Merkle root, adresse emettrice, chainId, tx_hash, horodatage d'emission, etat de confirmation". L'entite AnchorBatch existante ne contient pas de champ "adresse emettrice" (emitterAddress / senderAddress). Ce champ devra etre ajoute par PD-177. Le test TC-177-06 verifie la presence de ces champs mais n'asserte pas explicitement que l'adresse emettrice est celle du wallet actif au moment de l'emission (cette assertion est couverte par TC-177-08). Le probleme est que si le champ n'existe pas encore dans le schema, le test TC-177-06 validera une structure incomplete tant que la migration n'est pas ecrite — mais la spec ne mentionne pas explicitement qu'un nouveau champ doit etre ajoute. Impact : Risque d'implementation ou le champ adresse emettrice est journalise dans les logs mais pas persiste dans le registre append-only, violant INV-177-13 en production. Gravite : Majeur
ECART-06¶
Type : Hypothese dangereuse Reference : Spec §9 HYP-177-06 — "La politique de confirmations reseau applicable est deja definie dans le cadre transactionnel existant" Description : L'hypothese est fausse en pratique. Le code existant contient trois valeurs contradictoires de seuil de confirmations : 1. CONFIRMATION_BLOCKS = 12 (hardcode processor, non configurable) 2. ANCHOR_CONFIRMATION_BLOCKS = 30 (constante exportee dans anchor.constants.ts) 3. confirmations: 5 (Polygon) / confirmations: 1 (Arbitrum) dans blockchain.config.ts Le processor utilise CONFIRMATION_BLOCKS = 12 et ignore les valeurs de configuration reseau. Le test TC-177-05 repose sur "la fenetre configuree" sans savoir laquelle. Impact : L'hypothese masque un defaut existant qui rend INV-177-16 non verifiable. Toute implementation basee sur cette hypothese heritera de l'incoherence. Gravite : Bloquant
ECART-07¶
Type : Hypothese dangereuse Reference : Spec §5 FN-177-02 etape 4 + code existant blockchain-adapter.service.ts:106-108 Description : Le BlockchainAdapterService.waitForConfirmation() hardcode network: NetworkType = 'polygon' avec le commentaire "simplification: on utilise polygon par defaut". La spec PD-177 mentionne Polygon et Arbitrum comme reseaux cibles (HYP-177-02). La transaction pourrait etre soumise sur Arbitrum (chainId 421614) mais la confirmation serait attendue sur les noeuds Polygon — ce qui ne confirmera jamais. Impact : Un ancrage sur Arbitrum ne sera jamais confirme, violant FN-177-02 et INV-177-16. Gravite : Bloquant
ECART-08¶
Type : Ambiguite Reference : Spec §4 INV-177-09 — "Toute tentative de fuite de secret detectee doit produire un echec explicite et tracer un evenement d'incident securite" Description : Le mecanisme de detection de fuite de secret existant dans BlockchainError (lignes 47-54) effectue un console.warn() mais ne bloque pas l'operation (// Log warning mais ne bloque pas (défense en profondeur)). Cela contredit directement INV-177-09 qui exige un "echec explicite". Le test TC-SEC-02 suppose un blocage actif, mais l'implementation existante est fail-open. Le learning contextuel [Crypto] indique : "Fail-closed is mandatory for security-critical features - permissive defaults for auth create acceptance gaps." Impact : L'implementation existante est fail-open pour la detection de secret dans BlockchainError. PD-177 exige fail-closed mais ne precise pas si l'existant doit etre modifie ou si un nouveau mecanisme est attendu. Gravite : Bloquant
ECART-09¶
Type : Incoh&erence Spec<->Tests Reference : Spec §8 ST-177-10 vs Tests §2 TC-177-15 — "Exercice de reprise wallet" Description : La spec ST-177-10 et le test TC-177-15 exigent qu'un "resultat horodate, signe operationnellement, et auditable est consigne". Le terme "signe operationnellement" est present dans ST-177-10 mais absent de TC-177-15 (qui dit "horodate et auditable"). Inversion : le CA-177-13 ne mentionne pas non plus la signature operationnelle, il dit "resultat consigne". Il y a donc 3 formulations differentes pour le meme critere : 1. ST-177-10 : "horodate, signe operationnellement, et auditable" 2. TC-177-15 : "consigne, horodate et auditable" 3. CA-177-13 : "documentee, versionnee, et un exercice de test aboutit a un resultat consigne" Impact : L'exigence de signature operationnelle (ST-177-10) n'est pas reprise dans le test ni dans le critere d'acceptation. Un auditeur pourrait rejeter le test pour non-couverture. Gravite : Majeur
ECART-10¶
Type : Non testable Reference : Spec §4 INV-177-10 — "Une compromission d'un serveur applicatif ne doit pas permettre, a elle seule, la signature arbitraire de transactions sans controle custody valide" Description : Le test TC-SEC-03 verifie qu'un "serveur applicatif compromis mais sans capacite custody valide" ne peut pas signer. Mais la notion de "compromission" n'est pas definie par la spec : quel niveau de compromission ? Acces root ? Acces au process Node.js ? Acces memoire ? Le test devrait definir un modele de menace precis pour etre reproductible. En pratique, dans le mode S2 (KMS), le serveur applicatif detient les credentials IAM pour appeler AWS KMS. Une compromission du serveur donne acces a ces credentials et donc a la capacite de signature via KMS. L'invariant tel que formule pourrait etre toujours viole en S2. Impact : Le test risque d'etre soit trivial (mock sans credentials = pas de signature, ce qui est tautologique), soit impossible a satisfaire sans architecture specifique (HSM, MFA sur appels KMS). Gravite : Majeur
ECART-11¶
Type : Incoh&erence Spec<->Tests Reference : Spec §6 ERR-177-01 vs Tests §4 TC-ERR-01 Description : TC-ERR-01 asserte "le code d'erreur explicite ERR-177-01 est trace". Or les codes d'erreur existants dans le codebase sont de type BlockchainErrorCode (enum : CUSTODY_MODE_INVALID, CUSTODY_MODE_MISSING, etc.) et non de type ERR-177-01. La spec utilise la convention ERR-177-XX pour les cas d'erreur mais ne precise pas le mapping vers les BlockchainErrorCode existants. Le test devrait-il verifier ERR-177-01 (convention spec) ou CUSTODY_MODE_INVALID (code existant) ? Impact : Le test tel que redige n'est pas directement implementable sans convention de mapping entre les deux nomenclatures. Gravite : Majeur
ECART-12¶
Type : Ambiguite Reference : Spec §4 INV-177-03 — "Le wallet est utilise exclusivement pour des transactions d'ancrage probatoire (aucun usage metier tiers)" Description : Le test TC-177-09 tente de verifier cette exclusivite en soumettant "des requetes d'usage non-ancrage (DEX/DeFi, ERC-20, transfert metier tiers)" et en verifiant leur rejet. Cependant, la spec ne definit pas comment le systeme distingue une transaction d'ancrage d'une transaction non-ancrage. Dans le code existant, submitMerkleRoot() est la seule methode d'emission — il n'y a pas de mecanisme de filtrage generique. L'exclusivite est assuree par l'absence de code d'emission alternatif, pas par un controle actif. Le test TC-177-09 mentionne "endpoints, files de jobs et commandes" mais ne precise pas quelles requetes concretes sont tentees, ni quel mecanisme de rejet est attendu (HTTP 403 ? Absence de route ? Exception ?). Impact : Le test est non reproductible sans definition precise du vecteur d'attaque et du mecanisme de rejet. Gravite : Mineur
ECART-13¶
Type : Risque secu/conformite Reference : Spec §4 INV-177-08 + code existant BlockchainError.sanitizeContext() lignes 62-76 Description : La detection de secrets dans BlockchainError utilise des regex fixes : - ^0x[a-fA-F0-9]{64}$ — detecte les cles privees hex 256-bit avec prefixe 0x - ^[a-zA-Z0-9]{32,}$ — detecte les chaines alphanumeriques >= 32 chars - Pattern de mots-cles (mnemonic, seed, private, secret, password, token)
Cette detection ne couvre pas : - Les cles privees sans prefixe 0x (64 chars hex brut) - Les cles encodees en base64 - Les fragments de cles (split sur plusieurs champs) - Les secrets dans des objets imbriques (la sanitization est a un seul niveau de profondeur) INV-177-08 exige que la cle privee "ne doit jamais etre exportable en clair", mais le mecanisme de detection existant est poreux. Impact : Un secret pourrait fuiter dans les logs ou traces d'erreur si son format ne correspond pas aux patterns de sanitization actuels. Gravite : Majeur
ECART-14¶
Type : Hypothese dangereuse Reference : Spec §5 FN-177-02 etape 3-4 — atomicite signature/diffusion Description : Le flux FN-177-02 decrit la signature (etape 3) puis la diffusion (etape 4) comme deux etapes sequentielles distinctes. La spec ne precise pas le comportement attendu si la signature reussit mais la diffusion echoue (panne reseau entre les deux). Dans ce cas : - La transaction signee existe mais n'est pas diffusee. - Le nonce est "consomme" dans le contexte local. - Une rediffusion ulterieure pourrait etre necessaire. - Si le nonce est reutilise pour un autre ancrage, deux transactions en competition pourraient exister. Le code existant n'a pas de mecanisme de nonce management ni de transaction replay protection. Impact : Perte silencieuse d'un ancrage ou double-ancrage possible en cas de partition reseau entre signature et diffusion. Gravite : Majeur
ECART-15¶
Type : Incoh&erence Spec<->Tests Reference : Spec §4 INV-177-19 / TC-177-14 — rotation planifiee Description : TC-177-14 exige que "les preuves pre-rotation restent verifiables avec leur contexte historique d'adresse" et que "la transition n'introduit aucune rupture de chaine de preuve". Cependant, la spec ne definit pas comment le "contexte historique d'adresse" est persiste. L'entite AnchorBatch existante ne contient pas de champ d'adresse emettrice (voir ECART-05). Sans ce champ, la verification post-rotation est impossible car il n'y a aucun enregistrement de quelle adresse a signe quelle transaction. Le point CL-177-01 (politique de rotation) est d'ailleurs classe "a clarifier" et "NON TESTABLE" dans le document tests — mais TC-177-14 le teste quand meme. Impact : TC-177-14 couvre un scenario qui depend d'un point non clarifie et d'un champ inexistant. Gravite : Mineur
ECART-16¶
Type : Ambiguite Reference : Spec §4 INV-177-11 — "Les identifiants aleatoires techniques introduits par PD-177 doivent etre generes via crypto.randomUUID()" Description : Le code existant utilise deja crypto.randomUUID() dans de nombreux modules. La spec ne precise pas si cette exigence s'applique uniquement aux identifiants nouveaux introduits par PD-177, ou si elle couvre aussi les identifiants generes par les frameworks sous-jacents (TypeORM @PrimaryGeneratedColumn('uuid') qui utilise la generation UUID de PostgreSQL, pas de Node.js). Le test TC-177-16 mentionne une "verification statique + tests" mais ne precise pas le perimetre d'instrumentation. Impact : Un implementeur pourrait considerer que les UUID generes par PostgreSQL ne sont pas "introduits par PD-177" et les exclure du scope, ou inversement inclure tous les UUID du systeme, ce qui changerait significativement l'effort. Gravite : Mineur
ECART-17¶
Type : Risque secu/conformite Reference : Spec §5 FN-177-02 etape 4 + code existant blockchain-adapter.service.ts:67 Description : Dans le code existant, la transaction d'ancrage est envoyee au wallet lui-meme (to: walletAddress). Cela signifie que le wallet s'envoie une transaction a lui-meme avec le payload Merkle en data. Cette approche fait que : 1. Le wallet consomme du gas pour un self-transfer. 2. Il n'y a pas de contrat intelligent de destination pour valider le format du payload. 3. N'importe qui ayant l'adresse du wallet peut envoyer des donnees similaires au meme wallet, brouillant l'audit on-chain. La spec ne mentionne pas cette architecture self-transfer et ne definit pas le "format de payload valide" (FN-177-02 etape 2). Le format 0x + "MERKLE:" + root (ligne 148 du blockchain-adapter) n'est documente nulle part dans la spec. Impact : Un auditeur tiers (FN-177-03) pourrait avoir des difficultes a distinguer les transactions d'ancrage legimites des transactions parasites envoyees au meme wallet par des tiers, car il n'y a pas de contrat qui filtre. Gravite : Mineur
Matrice de couverture — Verification croisee¶
La matrice de couverture du document tests (§1) declare 21/21 invariants et 17/17 CA couverts. L'audit confirme cette couverture formelle mais identifie les lacunes qualitatives suivantes :
| Exigence | TC declares | Lacune qualitative |
|---|---|---|
| INV-177-13 | TC-177-06 | Champ adresse emettrice absent de l'entite existante (ECART-05) |
| INV-177-16 | TC-177-13, TC-ERR-05/06 | Seuil de confirmations indetermine (ECART-02/06) |
| INV-177-09 | TC-SEC-02 | Mecanisme existant est fail-open (ECART-08) |
| INV-177-10 | TC-SEC-03 | Modele de menace non defini (ECART-10) |
| INV-177-19 | TC-177-14 | Depend de CL-177-01 non clarifie (ECART-15) |
| INV-177-14 | TC-177-11 | "Registre append-only" non leve en ambiguite (ECART-04) |
Points "a clarifier" (CL-177-XX) — Evaluation d'impact¶
| Point | Impact sur testabilite |
|---|---|
| CL-177-01 | Bloque TC-177-14 (rotation) — scenario ecrit mais prerequis non formalise |
| CL-177-02 | Bloque un seuil quantitatif pour TC-ERR-03 (INSUFFICIENT_FUNDS — quel minimum ?) |
| CL-177-03 | Pas de separation testnet/mainnet formalisee, or le code a MAINNET_FORBIDDEN — interaction non documentee |
| CL-177-04 | Bloque la definition du RTO/RPO pour TC-177-15 (reprise) |
Verdict Gate 3¶
Statut : NON CONFORME — 4 bloquants a lever
Les 4 points bloquants (ECART-02, ECART-06, ECART-07, ECART-08) doivent etre resolus avant passage en implementation. Les points majeurs (8) representent des risques contractuels significatifs qui pourraient conduire a des rejets en recette ou en audit.
Bloquants a lever¶
- ECART-02 / ECART-06 : Formaliser la politique de confirmations applicable (quelle valeur fait foi parmi les 3 existantes) et l'inscrire dans la spec.
- ECART-07 : Le hardcode
network = 'polygon'danswaitForConfirmation()rend tout ancrage Arbitrum non confirmable. Ce defaut existant doit etre reconnu dans la spec (ou le perimetre Arbitrum retire). - ECART-08 : Clarifier si INV-177-09 exige de modifier le comportement fail-open existant de
BlockchainError.sanitizeContext()vers fail-closed, ou si un nouveau mecanisme de detection est attendu.