Aller au contenu

Confirmed. Now I have all the facts needed. Let me produce the structured audit report.


PD-103 — Revue d'acceptabilité (post-correction)

1. Références

  • Spécification : PD-103-specification.md (v3)
  • Tests contractuels : PD-103-tests.md (v3)
  • Acceptabilité existante : PD-103-acceptability.md
  • Date de revue : 2026-04-03
  • Reviewer : Claude (auditeur Gate 8 CLOSURE)

2. Suivi des écarts (append-only)

[2026-04-03] — Suivi E-01

  • Statut précédent : OUVERT (BLOQUANT)
  • Statut actuel : PARTIELLEMENT RÉSOLU
  • Justification factuelle :
  • SEALED ajouté dans NotifiableCaptureState (notifications.ts:38)
  • Contenu notification défini (notifications.ts:116-119)
  • isCaptureStateNotifiable() inclut SEALED (notifications.ts:214)
  • DIVERGENCE : le body de la notification est "Votre capture probatoire a été scellée et est en attente d'ancrage blockchain." alors que la spec §5.6 et TC-NOM-08 exigent exactement "Votre capture a été scellée avec succès. Preuve vérifiable disponible."
  • NON PROUVÉ : le déclenchement effectif à l'état SEALED n'est pas observable dans l'orchestrateur (orchestrator.ts s'arrête à UPLOADED). Le mécanisme de réception push backend → appel sendCaptureNotification('SEALED') n'est pas visible dans le code audité.
  • Preuve de vérification :
  • commit 5abf199 (app) — src/capture/notifications.ts (+12/-2)
  • Lecture directe notifications.ts:116-119 (message divergent)
  • Lecture directe orchestrator.ts (aucun appel notification à SEALED)

[2026-04-03] — Suivi E-02

  • Statut précédent : OUVERT (BLOQUANT)
  • Statut actuel : PARTIELLEMENT RÉSOLU
  • Justification factuelle :
  • Alignements réalisés : dek_wrapped_b64 TEXT (migration:52 ↔ entity:96), aes_gcm_nonce_b64 VARCHAR(32) (migration:54 ↔ entity:88), aes_gcm_tag_b64 VARCHAR(32) (migration:55 ↔ entity:91), colonnes skew_ms/reconciliation_attempts/last_reconciled_at ajoutées, seal_delayed_conformant_cycles renommé.
  • Audit log aligné : event_payload JSONB, user_id, http_status, source_ip.
  • BLOQUANT RÉSIDUEL : La contrainte CHECK ligne 104 de la migration référence seal_delayed_conforming_cycles (colonne inexistante) au lieu de seal_delayed_conformant_cycles (colonne réelle ligne 85). Le CREATE TABLE échouera à l'exécution.
  • MINEUR résiduel : Le COMMENT ON COLUMN ligne 169 référence dek_wrapped au lieu de dek_wrapped_b64.
  • MINEUR résiduel : Contrainte UNIQUE (capture_id) (migration:97) vs index unique composite ['userId', 'captureId'] (entity:46). Sémantique différente.
  • MINEUR résiduel : Default state migration 'CAPTURED' (ligne 47) vs entity PENDING_SEAL (ligne 133). Sans impact runtime (le service impose explicitement PENDING_SEAL), mais incohérence contractuelle.
  • Preuve de vérification :
  • commit 5228173 (backend) — 6 fichiers modifiés
  • Migration 1742400000000:104 seal_delayed_conforming_cycles ≠ colonne seal_delayed_conformant_cycles (ligne 85)
  • Migration 1742400000000:169 COMMENT ON COLUMN ...dek_wrapped ≠ colonne dek_wrapped_b64

[2026-04-03] — Suivi E-03

  • Statut précédent : OUVERT (BLOQUANT)
  • Statut actuel : RÉSOLU
  • Justification factuelle :
  • SEAL_DELAYED présent dans l'enum SQL capture_state_enum (migration:32)
  • SEAL_DELAYED = 'SEAL_DELAYED' présent dans l'enum TypeORM CaptureEventState (entity:25)
  • Colonne seal_delayed BOOLEAN + seal_delayed_conformant_cycles INTEGER présentes (migration:84-85, entity:146-151)
  • Service de réconciliation exploite cet état (capture-reconciliation.service.ts existe)
  • Preuve de vérification :
  • commit 5228173 (backend)
  • Migration 1742400000000:26-36 (enum 9 valeurs)
  • Entity capture-event.entity.ts:19-29 (enum 9 valeurs)

[2026-04-03] — Suivi E-04

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : RÉSOLU
  • Justification factuelle :
  • Décorateur @HttpCode(202) remplacé par @Res({ passthrough: true }) + res.status() conditionnel (controller:77-81)
  • result.idempotent ? HttpStatus.OK : HttpStatus.ACCEPTED implémente correctement le contrat §5.12
  • Service retourne idempotent: true sur replay (ingest.service.ts:171)
  • Preuve de vérification :
  • commit 5228173 (backend)
  • capture.controller.ts:77-81
  • capture-ingest.service.ts:152-171

[2026-04-03] — Suivi E-05

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : NON RÉSOLU
  • Justification factuelle :
  • _cancelOnCryptoError() (orchestrator.ts:510) passe { userCancelled: true } comme guard context pour une erreur crypto. INV-103-21 réserve CAPTURED → CANCELLED à l'annulation utilisateur explicite. La sémantique est incorrecte : une erreur crypto n'est pas une annulation utilisateur.
  • Preuve de vérification :
  • orchestrator.ts:510 — fsm.transition("CANCELLED", { userCancelled: true }) dans _cancelOnCryptoError

[2026-04-03] — Suivi E-06

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Non corrigé dans les commits 5abf199 / 5228173. Aucune modification visible de la cohérence session multipart ↔ objet backend.

[2026-04-03] — Suivi E-07

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • _cancelFromUploading() (orchestrator.ts:542) conditionne l'abort S3 à if (jwtToken). Si jwtToken est absent, l'abort est sauté. Pas de correction visible.

[2026-04-03] — Suivi E-08

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Divergence payload canonique spec v3 ↔ code-contracts.yaml non vérifiée dans cette revue (hors scope correction). Aucune modification visible.

[2026-04-03] — Suivi E-09

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Fichiers CaptureProgress.tsx (→ renommé CaptureProgressOverlay.tsx existant), tests app, module infra S3. Partiellement couvert par l'existence de CaptureProgressOverlay.tsx mais tests app et infra S3 non vérifiés dans cette revue.

[2026-04-03] — Suivi E-10

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Markdownlint non vérifiable dans cette revue.

[2026-04-03] — Suivi E-12

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Invariants hérités PD-105/106/107 non vérifiables dans le scope PD-103.

[2026-04-03] — Suivi T-01

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Guard @Roles('user') présent dans controller:45, mais test explicite vérifiant HTTP 403 + body + auditService.log non visible dans les commits de correction.

[2026-04-03] — Suivi T-02

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Test 401 absent de la matrice — non corrigé.

[2026-04-03] — Suivi S-01

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Confusion clé objet single ↔ multipart non corrigée dans les commits audités.

[2026-04-03] — Suivi S-02

  • Statut précédent : OUVERT (MAJEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • deferCapture() persiste dekWrappedB64 dans le store AsyncStorage (orchestrator.ts:598). Le champ DEK wrappé est chiffré (enveloppe RSA-OAEP), donc pas un DEK en clair. Cependant, ocr_text en clair dans deferredCaptures AsyncStorage reste un risque de fuite vie privée sur lockscreen/backup. Non corrigé.

[2026-04-03] — Suivi S-03

  • Statut précédent : OUVERT (MINEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • Notifications définies pour UPLOADED, UPLOAD_DEFERRED (états non terminaux). Risque divulgation lockscreen. Non corrigé.

[2026-04-03] — Suivi S-04

  • Statut précédent : OUVERT (MINEUR)
  • Statut actuel : OUVERT
  • Justification factuelle :
  • purgeStale() sans whitelist de préfixe. Non corrigé.

3. Verdict d'acceptabilité (courant)

Verdict actuel : ⛔ REFUSÉ Date : 2026-04-03 Motif synthétique : 1 BLOQUANT résiduel (E-02 : CHECK constraint chk_seal_delayed_cycles référence colonne inexistante seal_delayed_conforming_cycles — la migration SQL échouera à l'exécution). E-01 partiellement résolu (message notification divergent de la spec, déclenchement effectif non prouvé). 10 MAJEURS ouverts, 2 MINEURS ouverts.

4. Historique des verdicts

Date Verdict Version / commit Commentaire
2026-04-03 ⛔ REFUSÉ (initial) pre-correction 3 BLOQUANTS, 12 MAJEURS, 4 MINEURS
2026-04-03 ⛔ REFUSÉ (post-correction v1) 5abf199 (app) / 5228173 (backend) E-03 RÉSOLU, E-04 RÉSOLU, E-01 PARTIELLEMENT RÉSOLU, E-02 PARTIELLEMENT RÉSOLU (CHECK constraint SQL invalide). 1 BLOQUANT résiduel.

Synthèse des corrections requises avant re-soumission

Priorité Écart Action requise
BLOQUANT E-02 Corriger chk_seal_delayed_cycles : remplacer seal_delayed_conforming_cycles par seal_delayed_conformant_cycles (migration:104). Corriger COMMENT dek_wrappeddek_wrapped_b64 (migration:169).
BLOQUANT E-01 (a) Aligner le body notification SEALED avec la spec : "Votre capture a été scellée avec succès. Preuve vérifiable disponible." (notifications.ts:118). (b) Prouver le déclenchement effectif (handler push backend → sendCaptureNotification).