Aller au contenu

PD-276 — Rapport de confrontation (Étape 5 — Gate AMBIGUITY)

Ce rapport est produit par l'orchestrateur Claude avant la gate 5 PMO. Il confronte les documents produits pour identifier convergences, divergences et zones d'ombre.

1. Sources confrontées

Document Étape Version
PD-276-specification.md Étape 1 (v2 post-Gate 3) Spec consolidée avec corrections ECT/AMB
PD-276-tests.md Étape 2 (v2 post-Gate 3) Scénarios de tests contractuels
PD-276-plan.md (Implementation Plan) Étape 4 Plan d'implémentation
PD-276-code-contracts.yaml Étape 4 Frontières de code inter-agents

2. Convergences

  • C-01 Architecture Zero-Knowledge : Les 4 documents s'accordent unanimement sur l'interdiction de dérivation de clé serveur (INV-276-01). Le plan implémente via Argon2Service.validateParams() retournant accept/reject, les contracts interdisent toute dépendance argon2 native, les tests vérifient l'absence d'artefact de dérivation (TC-INV-01). Aucune ambiguïté.

  • C-02 Bornes Argon2id contractuelles : Spec (INV-276-02/03), tests (TC-NOM-01/02, TC-ERR-01 à 05), plan (C1, ARGON2_CONFIG) et contracts (module pd276-argon2-service) sont parfaitement alignés sur les valeurs : memory [65536, 1048576], iterations [3, 10], parallelism [4, 16], type [2, 2], hashLength [32, 32]. Validation atomique confirmée dans les 4 sources.

  • C-03 Metadata binding HMAC-SHA256 / 32 bytes : Spec (INV-276-05), tests (TC-NOM-04), plan (C3) et contracts (module pd276-metadata-binding) s'accordent sur HMAC-SHA256 produisant 32 bytes, avec vérification avant accès.

  • C-04 Contexte HKDF strict : Les 4 documents convergent sur 'ProbatioVault::MetadataBinding::v1' comme contexte HKDF pour la dérivation de K_binding (INV-276-06). Le plan et les contracts exigent une centralisation en constante (jamais inline). Tests (TC-INV-02) vérifient le contexte exact.

  • C-05 HTTP 422 sur tag invalide : Spec (E-06), tests (TC-ERR-07, TC-NEG-01), plan (§6) et contracts (C5) s'accordent sur HTTP 422 UnprocessableEntityException avec SLA < 100ms.

  • C-06 Migration 2 phases : Phase 1 BYTEA NULL, phase 2 NOT NULL + CHECK octet_length = 32. Réversibilité documentée. Convergence complète entre spec (§5), tests (TC-NOM-06/07/11, TC-NR-04), plan (C6) et contracts (module pd276-migrations).

  • C-07 Machine à états binding : Trois états (LEGACY_NULL_TAG, BOUND_TAG_VALID, TAMPERED_TAG_INVALID), transitions autorisées/interdites identiques dans les 4 documents. TAMPERED terminal confirmé partout. Transition LEGACY → TAMPERED couverte (TC-NOM-13).

  • C-08 Politique LEGACY phase 1 : Lecture seule autorisée, warning loggé, rotation recommandée non forcée. Convergence spec (CA-276-13), tests (TC-NOM-12, TC-NR-03), plan (C5 logique unwrapEnvelope), contracts (C5 invariants).

  • C-09 Non-régression 21 checks existants : Aucune modification de pv_envelope_compliance.pl. Comparatif baseline OK/KO avant/après. Convergence spec (INV-276-10), tests (TC-NR-01, TC-ERR-09), plan (§3) et contracts (C6 forbidden).

  • C-10 Zeroization et timing-safe : Plan et contracts convergent sur crypto.timingSafeEqual() pour la comparaison de tags et zeroization de K_binding dans finally. Pattern existant (AesKwService) référencé.

  • C-11 Couverture tests : 38 scénarios (13 NOM + 11 ERR + 4 INV + 6 NEG + 4 NR). Matrice de couverture tests ↔ invariants complète. Mapping plan §5 cohérent avec matrice tests §2.

3. Divergences

Les conflits ne doivent JAMAIS être lissés. Chaque divergence est rendue visible.

  • DIV-01 : Fait Prolog requis — validateParams (spec) vs deriveKey (plan/contracts/réalité Prolog)
  • Spec (INV-276-09) : « Les faits formels requis existent : service_method(argon2, validateParams) »
  • Plan (§9.1, §1 C1) : Les checks Prolog 10 et 22 consomment service_method(argon2, deriveKey), pas validateParams. Le plan résout via un alias public deriveKey()validateParams().
  • Code Contracts (C1) : Documentes explicitement deriveKey() comme alias pour conformité Prolog.
  • Tests (TC-INV-04, §2 matrice) : Contradiction interne — le corps du test TC-INV-04 affirme que les 3 faits incluent service_method(argon2, validateParams), mais le mapping du plan (§5) indique que check_10 et check_22 consomment service_method(argon2, deriveKey).
  • Impact : Si TC-INV-04 est implémenté tel que rédigé dans le document de tests (vérifiant validateParams au lieu de deriveKey), le test passera (le fait est émis) mais ne validera pas la dépendance réelle des checks Prolog. L'objectif du test — prouver l'alignement faits↔checks — serait manqué. Divergence MAJEURE entre spec/tests et réalité Prolog, correctement identifiée et résolue dans le plan, mais les documents amont (spec + tests) n'ont pas été corrigés.

  • DIV-02 : Concaténation canonique — séparateur null byte absent de la spec

  • Spec (INV-276-05) : « calculé sur algorithm || version || envelope_type || device_id » — aucune mention de séparateur.
  • Plan (C3) : « Concaténation canonique : Buffer.concat([algorithm, version, envelope_type, device_id]) — chaque champ encodé UTF-8 avec séparateur null byte »
  • Code Contracts (C3) : « Concaténation canonique = algorithm || NUL || version || NUL || envelope_type || NUL || device_id »
  • Tests : Aucun test ne vérifie explicitement le format de concaténation.
  • Impact : Sans séparateur, des valeurs de champs juxtaposées pourraient produire des collisions de binding (ex: algo="AES", version="256" identique à algo="AES2", version="56"). Le plan et les contracts corrigent implicitement ce risque, mais la spec ne le contractualise pas. Divergence MOYENNE — le contrat de sérialisation n'est pas formellement aligné.

  • DIV-03 : Rattachement TC-NOM-04 — critère d'acceptation incorrect

  • Tests (matrice §2) : TC-NOM-04 est rattaché à CA-276-08 (« Metadata binding vérifié avant accès »).
  • Plan (§11 #4) : Identifie que TC-NOM-04 teste la création (computeTag), pas le contrôle d'accès.
  • Spec (CA-276-08) : « Tag invalide => accès refusé (HTTP 422) » — concerne la vérification, pas la création.
  • Impact : Traçabilité fragilisée. TC-NOM-04 devrait être rattaché à CA-276-07 ou CA-276-09 (création/persistance du tag). Divergence MINEURE — pas de conséquence fonctionnelle, mais la matrice de couverture est inexacte.

  • DIV-04 : Transaction SERIALIZABLE — exigence plan/contracts absente de la spec

  • Plan (§7.1) : « Transactions SERIALIZABLE sur vérification + transition TAMPERED » pour prévenir les race conditions.
  • Code Contracts (C5) : « Transaction SERIALIZABLE pour vérification + transition d'état TAMPERED »
  • Spec : Aucune mention d'isolation transactionnelle pour la transition d'état.
  • Tests : Aucun test de concurrence (race condition) n'est défini.
  • Impact : Le plan introduit une exigence technique absente de la spec. Si un agent implémente sans SERIALIZABLE, la spec ne le détectera pas, mais un double unwrap concurrent pourrait contourner la détection de tampering. Divergence MOYENNE — le plan ajoute un mécanisme de sécurité non contractualisé dans la spec.

  • DIV-05 : Nombre de faits Prolog — 3 (spec/tests) vs 5 (plan)

  • Spec (INV-276-09) : 3 faits requis explicitement.
  • Tests (TC-INV-04) : Vérifie 3 faits.
  • Plan (C7) : Le générateur émettra 5 faits (service, service_method × 3 [validateParams, deriveKey, getConfig], entity_column). Les faits supplémentaires (service_method(argon2, getConfig) et service_method(argon2, deriveKey)) ne sont pas dans la spec.
  • Impact : Pas de risque fonctionnel (faits supplémentaires non consommés par des checks échouants). Mais le fait service_method(argon2, deriveKey) est critique pour que les checks 10/22 passent, et il n'est pas dans la liste contractuelle de la spec. Divergence MOYENNE — le fait le plus critique pour le 24/24 n'est pas contractualisé dans INV-276-09.

4. Zones d'ombre

  • ZO-01 Q-276-01 toujours ouvert : Les bornes maximales Argon2 (memory <= 1048576 KiB, iterations <= 10, parallelism <= 16) n'ont pas été validées par le produit/sécurité. Le plan (H-276-08) assume la stabilité pour cette story. Si les bornes changent après implémentation, seule la constante ARGON2_CONFIG est à modifier — impact limité mais non nul.

  • ZO-02 Q-276-05 toujours ouvert : Le nom exact du script/générateur de faits Prolog en CI n'est pas confirmé. Le plan référence extract-facts.py (C7) et le code contracts mentionne des hypothèses sur le parsing regex (H-276-03). Si le script en CI utilise un nom ou un mécanisme différent, les faits pourraient ne pas être émis.

  • ZO-03 Backfill LEGACY — mécanisme opérationnel non spécifié : Le plan (§9.2) identifie que le backfill nécessite un flux applicatif (lazy migration) car K_master_user n'est jamais en clair. La spec mentionne le backfill comme prérequis de la phase 2 mais ne spécifie aucun mécanisme. Les tests (TC-NOM-10) testent la transition LEGACY → BOUND mais pas le déclenchement du lazy backfill. Le plan déclare le backfill hors périmètre du code livré (§10). Aucun test ne couvre le scénario de production où le backfill est incomplet et la phase 2 ne peut être activée.

  • ZO-04 Observabilité — journaux signés prérequis externe : La spec (H-276-07) et le plan (§11 #5) reconnaissent que les journaux d'audit signés sont un prérequis couvert par d'autres stories. Les tests (TC-NOM-01 THEN, TC-NOM-05 THEN) vérifient des traces d'audit mais ne spécifient pas si ces traces sont signées ou non. Si le Logger NestJS existant ne produit pas de traces signées, l'observabilité probatoire sera affaiblie.

  • ZO-05 Endpoint configuration — authentification/autorisation : La spec (Flux F2) mentionne « appel API interne/externe autorisé ». Le plan (C2) définit GET /crypto/argon2/config mais ne spécifie aucun guard d'authentification. Les code contracts (C2) ne mentionnent pas de guard. Aucun test ne couvre l'accès non autorisé à cet endpoint. La configuration Argon2 exposée n'est pas un secret (bornes publiques OWASP), mais l'absence de guard contraste avec les conventions habituelles du projet.

  • ZO-06 Périmètre unwrapEnvelope — readOnly flag : Le plan (C5 logique) introduit un flag readOnly retourné quand l'enveloppe est LEGACY en phase 1. Ce mécanisme n'est pas dans la spec ni les tests. Comment les appelants de unwrapEnvelope savent-ils qu'ils doivent respecter ce flag ? Le plan ne documente pas le contrat d'interface modifié pour les appelants.

5. Recommandation

  • Procéder — convergence confirmée, aucun conflit bloquant
  • Rework nécessaire — divergences à résoudre avant de continuer
  • Escalade — décision humaine requise sur un point structurant

Justification :

  • DIV-01 est une divergence MAJEURE : le fait service_method(argon2, deriveKey) est la clé du passage 24/24 checks mais n'est contractualisé ni dans la spec (INV-276-09) ni vérifié correctement dans TC-INV-04. Le plan a identifié et résolu le problème, mais les documents amont sont incohérents. Correction recommandée : aligner INV-276-09 et TC-INV-04 sur la réalité Prolog (deriveKey).

  • DIV-02 est une divergence MOYENNE structurante : le format de concaténation canonique doit être contractualisé dans la spec pour éviter des collisions de binding. Correction recommandée : expliciter le séparateur null byte dans INV-276-05.

  • DIV-05 est une divergence MOYENNE corrélée à DIV-01 : le fait critique deriveKey manque dans INV-276-09.

Les autres divergences (DIV-03, DIV-04) et zones d'ombre (ZO-01 à ZO-06) sont documentées mais non bloquantes pour la gate.