PD-286 — Acceptabilité¶
Prérequis acceptabilité¶
- Tests CI : 380/380 PASS (26 suites,
npx jest --testPathPattern=export) - Coverage : module-level coverage (222 tests PD-286 + 158 tests PD-85 non-régression)
- TODO non tracés : aucun identifié
- Code DEV ONLY : aucun
Phase 1 — Reviews automatisées¶
| Check | Résultat | Détail |
|---|---|---|
| ESLint | OK | 0 error, 0 warning sur src/modules/export/ |
| TypeScript | OK | 0 error PD-286 (1 error pré-existante merkle/v2 hors scope) |
| Tests | OK | 380/380 PASS (2.0s) |
| Non-régression PD-85 | OK | 10/10 tests ExportService originaux passent avec nouveaux mocks |
Phase 1.5 — Analyse Sonar¶
- Quality Gate : SKIP (Docker indisponible, sonar-scanner non installé — capability probe: docker=unavailable)
- Mitigation : ESLint + TSC couvrent les règles Sonar critiques (security hotspots, type safety). Sonar sera validé en pipeline CI post-merge.
Phase 2 — Reviews LLM (Codex/ChatGPT)¶
Review Code (7a)¶
Codex a analysé les 11 modules en mode agentic. Points identifiés : - MAJEUR : ExportSession.totalSizeBytes stocké comme String alors que c'est un nombre — risque de comparaison string vs number. Mitigation : le champ est utilisé uniquement pour l'audit, jamais pour la logique métier. - MAJEUR : buildPartitionInput utilise un fallback 1 MB par preuve (HT-03 PD-85) — les vrais bytes sont vérifiés côté app par le hash du manifest. - MINEUR : Quelques assertions as TypeScript dans les DTOs (branded types). - Aucun BLOQUANT identifié.
Review Tests (7b)¶
- MAJEUR : Tests d'intégration E2E (DB réelle + app pipeline complet) non inclus dans ce scope — couverts par C11 en staging.
- MINEUR : Certains tests mockent
ExportSessionsave sans vérifier les valeurs persistées. - Aucun BLOQUANT identifié.
Review Sécurité (7c)¶
- MAJEUR : L'endpoint
POST /exports/complaint-filene limite pas le nombre de preuves atomiques > VOLUME_MAX_BYTES — un attaquant pourrait demander 13 volumes dédiés de 768 MB chacun. Mitigation : MAX_TOTAL_EXPORT_BYTES (10 GB) + MAX_PROOF_IDS (500) + rate limiting existant. - MAJEUR : Les signed URLs ont un TTL configurable mais pas de revocation mechanism — risque si session compromise pendant le téléchargement multi-volumes. Mitigation : TTL borné [1, 30] min (INV-85-04), S3 bucket policy.
- MINEUR :
integrityHashcomparé avec===au lieu detimingSafeEqual— acceptable car le hash n'est pas un secret (spec §5.1 note). - Aucun BLOQUANT identifié.
Synthèse¶
| Critère | Score |
|---|---|
| Tests | 380/380 PASS, non-régression PD-85 OK |
| Lint/TSC | Clean |
| Sonar | SKIP (docker unavailable) — mitigé par ESLint |
| Review Code | 0 BLOQUANT, 2 MAJEURS (fallback bytes, totalSizeBytes string) |
| Review Tests | 0 BLOQUANT, 1 MAJEUR (E2E staging-only) |
| Review Sécurité | 0 BLOQUANT, 2 MAJEURS (volume count abuse, signed URL revocation) |
Verdict acceptabilité : ACCEPTABLE avec réserves (0 bloquant, 5 majeurs mitigés, 3 mineurs).
Les 5 MAJEURs ont tous des mitigations documentées et ne constituent pas des risques de sécurité immédiats.
Corrections post-Gate 8 (réserves DIV-03, DIV-04)¶
Les réserves identifiées en Gate 8 RESERVE (8.25/10) ont été corrigées :
DIV-03 — C6 ExportAuditService câblée dans C5 (corrigé)¶
ExportService n'appelle plus auditLogService directement. Tous les appels audit passent par la façade ExportAuditService : - appendStart() — dans persistSessionAndAudit() (transitions REQUESTED → PLANNED_*) - appendRejection() — pour les rejets métier (ALL_PROOFS_REJECTED, TOTAL_LIMIT_EXCEEDED, PROOF_TOO_LARGE, INTERNAL) - appendFinal() — dans le nouvel endpoint de finalisation
Les forbidden C6 (anti-signedUrl/manifest dans metadata, codes uniformes anti-énumération) sont maintenant appliqués en production.
DIV-04 — Endpoint de finalisation app → backend (corrigé)¶
Nouvel endpoint POST /exports/:exportId/finalize : - L'app appelle avec {finalStatus: "COMPLETED"} après assemblage du .pvproof - L'app appelle avec {finalStatus: "FAILED", reasonCode: "HASH_MISMATCH"} en cas d'échec - Le backend vérifie l'ownership, transite la state machine, et émet l'audit FINAL via ExportAuditService.appendFinal() (INV-286-09) - Anti-énumération : 404 uniforme si export inconnu OU non détenu
Vérifications post-correction¶
- Tests : 380/380 PASS (aucune régression)
- TypeScript : 0 erreur PD-286
- ESLint : clean
integrityHashespersistés surExportSessionpour que l'audit FINAL (COMPLETED/FAILED/EXPIRED) puisse les inclure