Aller au contenu

PD-280 — Revue Sécurité

Résumé

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

Verdict : ❌ NON_CONFORME

Audit des forbidden patterns

Pattern interdit Recherché Trouvé
Ajouter une 5e valeur a CheckResult sans modification spec et TLA+ CheckResult contient seulement 4 valeurs
Exposer la string PENDING dans linkResults[*] cote API Mapping + interceptor
Rendre pendingReason/nextCheckAfterSeconds optionnels en PENDING Interceptor validateConditionalPresence
Muter l'etat metier lors d'un appel de verification ProofVerificationService.verify() fait uniquement des findOne
Permettre transition DONE -> PENDING ou terminal -> PENDING Pas de garde forte hors proofFinalized + projection permissive
Accepter un proofId non UUID v4 ParseUUIDPipe({ version: '4' })
Creer ou muter une ressource via GET Contrôleur GET read-only
Enregistrer l'intercepteur comme APP_GUARD global Interceptor attaché à la route (@UseInterceptors)
Laisser passer une reponse incoherente vers le client Interceptor ne rejette pas verificationStatus inconnu / payload non objet
Logger sans requestId/proofId de correlation Logs contiennent proofId, pas requestId
Accepter une valeur pendingReason hors enum Validation via VALID_PENDING_REASONS

Tentatives de bypass

Attaque Résultat Commentaire
GET /proofs/not-a-uuid/verification Bloqué (400) ParseUUIDPipe v4 correctement configuré
Injection champ (GET sans body) Non applicable Pas de surface body/query utilisée ici
JWT user A sur proof de B ⚠️ Potentiel bypass Service filtre par proofId uniquement, pas de check ownership explicite dans le code audité
Requête sans JWT Bloqué (401 attendu) @UseGuards(JwtAuthGuard, AuthorizationGuard) présent
Manipulation SLA/time tampering Pas de mutation DB Conforme à INV-280-09, mais la projection peut masquer des états invalides

Vulnérabilités identifiées

ID Description Gravité Fichier
S-01 IDOR/cross-tenant potentiel: verify() charge la preuve par proofId sans contrainte explicite d’appartenance utilisateur (userId/mandate scope) dans la requête; si RLS est mal configuré ou contourné, accès horizontal possible CRITIQUE (conditionnel RLS) src/modules/integrity/services/proof-verification.service.ts
S-02 Bypass de contrat de sortie: l’interceptor n’impose pas strictement verificationStatus ∈ {PENDING,DONE} et retourne silencieusement si payload non objet; une réponse incohérente peut passer MAJEUR src/modules/integrity/interceptors/verification-contract.interceptor.ts
S-03 Masquage d’état invalide: valeurs DB hors enum CheckResult sont castées puis mappées en null (mapLinkValue fallback), ce qui peut simuler un PENDING valide et contourner des invariants métier MAJEUR src/modules/integrity/services/proof-verification-mapper.service.ts
S-04 INV-280-04 contournable par données corrompues: garde finalization bloque seulement la string exacte 'PENDING'; un état illégal (ex. 'BROKEN') en preuve finalisée n’est pas bloqué et peut finir exposé en null MAJEUR src/modules/integrity/services/proof-verification.service.ts, src/modules/integrity/services/proof-verification-mapper.service.ts
S-05 Contraintes DB insuffisantes: pas de CHECK sur chain_* ni FK explicite proof_id; facilite l’injection d’états invalides/orphelins côté DB et fragilise la barrière primaire MAJEUR src/database/migrations/1741400000000-PD-280-AddProofVerificationJobs.ts, src/modules/integrity/entities/proof-verification-job.entity.ts
S-06 Corrélation logs incomplète: erreurs de contrat loguées sans requestId, ce qui réduit la traçabilité incident/réponse MINEUR src/modules/integrity/interceptors/verification-contract.interceptor.ts

Recommandations

  • Ajouter une barrière d’autorisation primaire explicite dans verify() (ownership/tenant scope), même si RLS existe (defense in depth).
  • Rendre l’interceptor fail-closed strict: rejeter si payload non objet, verificationStatus inconnu, verificationRequestId non UUID v4.
  • Supprimer le fallback permissif mapLinkValue -> null pour valeurs inconnues: lever VERIFICATION_CONTRACT_BROKEN immédiatement.
  • Renforcer guardFinalization() pour rejeter tout état non terminal autorisé (OK|KO|INDETERMINATE) quand proofFinalized=true.
  • En DB: ajouter CHECK constraints sur chain_document/merkle/tsa/blockchain, FK proof_id vers la table preuve, et idéalement index/contrainte d’unicité métier par proof_id si attendu.
  • Uniformiser les logs d’erreur avec requestId + proofId (+ verificationRequestId quand disponible) pour forensique/API incident response.