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,
verificationStatusinconnu,verificationRequestIdnon UUID v4. - Supprimer le fallback permissif
mapLinkValue -> nullpour valeurs inconnues: leverVERIFICATION_CONTRACT_BROKENimmédiatement. - Renforcer
guardFinalization()pour rejeter tout état non terminal autorisé (OK|KO|INDETERMINATE) quandproofFinalized=true. - En DB: ajouter CHECK constraints sur
chain_document/merkle/tsa/blockchain, FKproof_idvers la table preuve, et idéalement index/contrainte d’unicité métier parproof_idsi attendu. - Uniformiser les logs d’erreur avec
requestId+proofId(+verificationRequestIdquand disponible) pour forensique/API incident response.