Aller au contenu

PD-250 — Revue Sécurité

Résumé

Critère Statut
Forbidden patterns
Injection SQL
Auth/Authz ⚠️
Fuite données
Validation ⚠️

Verdict : ⚠️ RÉSERVES

Barrières primaires identifiées et effet d'atténuation :

  • Auth primaire : OidcJwtAuthGuard + AuthorizationGuard sur l'endpoint admin (401/403), ce qui bloque les bypass simples sans JWT/role.
  • Barrières probatoires : signature HSM + horodatage TSA + machine d'état terminale (DESTROYED/RECONCILIATION_FAILED) ; plusieurs écarts observés restent sur des couches secondaires (observabilité/contrat), pas sur une compromission directe de clé.
  • WORM/conservation : la conservation du bordereau repose sur des invariants de statut/stockage ; l'absence d'un filtre défensif explicite (entity_type != 'BORDEREAU') est un écart de defense-in-depth.

Audit des forbidden patterns

Pattern interdit Recherché Trouvé
allowUnknown:false requis dans Joi config src/modules/destruction/config/destruction.config.ts ❌ (allowUnknown: true)
.default(trackedDefault(...)) pour paramètres bornés src/modules/destruction/config/destruction.config.ts ❌ (.default(...) direct)
Exclusion explicite des bordereaux de la sélection (entity_type != 'BORDEREAU') src/modules/destruction/services/eligibility.service.ts
API BullMQ dépréciées (getRepeatableJobs/removeRepeatableByKey) src/modules/destruction/** ✅ (absentes hors tests contractuels)
Queue name contenant : src/modules/destruction/config/destruction.config.ts

Tentatives de bypass

Attaque Résultat Commentaire
Injection champ protégé { "email": "hack@evil.com" } N/A Aucun endpoint PD-250 ne permet une mutation directe d'entité via body utilisateur.
Injection SQL batchId="'; DROP TABLE--" Échappé batchId validé @IsUUID('4') + requête paramétrée (:batchId) dans BordereauController.
Fuite champ sensible sur GET admin bordereaux Non observée Réponse limitée à bordereauId/batchId/createdAt/documentCount/pdfUrl; pas de PII directe.
Cross-access JWT user A sur données B Refusé Endpoint sous OidcJwtAuthGuard + AuthorizationGuard + @Roles('admin').
Bypass auth sans JWT Refusé (401) Bloqué par OidcJwtAuthGuard.

Vulnérabilités identifiées

ID Description Gravité Fichier
S-01 Absence d'audit d'accès refusé (403) pour l'endpoint admin bordereaux. Vecteur : un acteur non-admin répète des appels sur /admin/bordereaux; l'accès est bloqué mais l'événement destruction.access_denied n'est pas tracé côté domaine destruction, réduisant la détection/forensic (INV-250-15 partiellement couvert). MINEUR src/modules/destruction/controllers/bordereau.controller.ts
S-02 Filtre défensif manquant entity_type != 'BORDEREAU' dans la sélection d'éligibilité. Vecteur : si un incident amont (bug/migration/écriture admin) fait dériver un bordereau vers un état temporellement éligible, le job peut le sélectionner faute d'exclusion explicite. Impact principal : risque de suppression de preuve en cas de rupture d'un invariant amont; atténué par les barrières WORM/statut. MINEUR src/modules/destruction/services/eligibility.service.ts
S-03 Violation de contrat de validation config (allowUnknown: true, absence de trackedDefault). Vecteur : dérive de configuration non tracée (variables inattendues), réduisant la capacité de détection d'un mauvais durcissement en exploitation. MINEUR src/modules/destruction/config/destruction.config.ts

Écarts documentés (mock/stub, non classés MAJEUR)

ID Description Gravité Fichier
D-01 Stub TSA explicite (TODO(PD-250)): le factory TSA_TIMESTAMP_SERVICE rejette systématiquement. Effet : fail-closed (pas de destruction), impact disponibilité/conformité mais pas bypass sécurité direct. MINEUR (écart documenté) src/modules/destruction/destruction.module.ts
D-02 S3Service est un stub TODO (upload/delete en succès simulé). Effet : en environnement non mocké, comportement non réaliste ; à traiter avant production. MINEUR (écart documenté) src/modules/storage/s3.service.ts

Recommandations

  • Implémenter un audit explicite des refus d'accès admin (destruction.access_denied) via guard dédié ou exception filter domaine destruction (conforme INV-250-15).
  • Ajouter l'exclusion explicite entity_type != 'BORDEREAU' dans selectEligible et reVerifyEligibility pour verrouiller INV-250-06 en defense-in-depth.
  • Aligner destruction.config sur le contrat : allowUnknown: false, defaults via trackedDefault(...), et test contractuel bloquant CI.
  • Remplacer les stubs TSA/S3 par des adapters réels avant mise en prod, puis rejouer TC-250-04/05/06/08/08b/20 en intégration.