Skip to content

ADR 020 — Vuln management : VEX pour l'inexploitable, risk acceptance pour l'exploitable acceptĂ© (2026-06-19)

Statut

Accepté (2026-06-19) et validé hors infra (scan Trivy local : la CVE migrée en VEX est bien neutralisée par --vex, le gate reste vert). Premier ADR du projet transverse de gestion continue des vulnérabilités.

Le comment (pédagogique) est dans le guide Comprendre le vuln management.

Contexte

Le scan de sécurité est aujourd'hui build-time : Trivy (fs + image) bloque au build les CVE HIGH/CRITICAL fixables. Quand une CVE est fixée upstream mais qu'on ne peut pas la patcher (le correctif casserait une dépendance), on l'a jusqu'ici mise dans .trivyignore (cas vécu #95/#96 : deux CVE starlette).

Limite de cette approche : .trivyignore mĂ©lange deux situations trĂšs diffĂ©rentes sous une mĂȘme « liste Ă  taire ».

  • Une CVE peut ĂȘtre prĂ©sente mais inexploitable (le code vulnĂ©rable n'est jamais atteint par l'application). La taire est lĂ©gitime, mais en commentaire libre, sans format standard ni traçabilitĂ©.
  • Une CVE peut ĂȘtre rĂ©ellement atteignable, et on choisit d'accepter le risque (impact limitĂ©, pas de correctif applicable). La taire « comme les autres » masque le fait qu'on porte un vrai risque.

Mettre les deux dans le mĂȘme fichier, avec la mĂȘme mĂ©canique, empĂȘche de distinguer « je prouve que je ne suis pas concernĂ© » de « je suis concernĂ© mais j'assume ».

DĂ©cision 1 — VEX (OpenVEX) pour les CVE non exploitables

On déclare les CVE non exploitables dans un document VEX (Vulnerability Exploitability eXchange) au format OpenVEX, .vex/openvex.json, consommé par Trivy via --vex.

  • Statut not_affected + justification dans un vocabulaire fermĂ©, machine-lisible (ex. vulnerable_code_not_in_execute_path).
  • Premier cas : CVE-2026-48818 (starlette, SSRF/NTLM via StaticFiles). L'application n'enregistre aucun StaticFiles et n'appelle aucun mount() (vĂ©rifiĂ© par revue de code) : le code vulnĂ©rable n'est jamais dans le chemin d'exĂ©cution → not_affected / vulnerable_code_not_in_execute_path.
  • Format standard et interopĂ©rable (rĂ©utilisable par d'autres scanners), avec timestamp (date de la dĂ©claration) → auditabilitĂ©.

Alternative écartée : garder ces CVE dans .trivyignore. Rejeté car non standard, justification en commentaire libre, et surtout : confond inexploitabilité prouvée et risque accepté.

DĂ©cision 2 — Risk acceptance documentĂ©e pour l'exploitable acceptĂ©

Une CVE atteignable mais dont on accepte le risque reste dans .trivyignore, mais explicitement étiquetée « risque accepté » avec :

  • la preuve d'atteignabilitĂ© (oĂč le code vulnĂ©rable est appelĂ©),
  • l'analyse d'impact (pourquoi le risque est tolĂ©rable),
  • une date de revue et le lien vers l'issue de dette.

Cas : CVE-2026-54283 (starlette, DoS via request.form()). Le /login utilise OAuth2PasswordRequestForm (confirmé app/routers/auth.py), donc le code est atteignable. Impact limité (DoS CPU sur un endpoint, ni fuite ni RCE). Risque accepté, revue au 2026-09-19 ou au déblocage de #96.

On n'utilise pas un VEX not_affected pour ce cas : ce serait déclarer faussement une inexploitabilité. VEX = exploitabilité, pas acceptation de risque.

Conséquences

  • .trivyignore ne contient plus que : (1) CVE OS Debian unfixed (documentation, dĂ©jĂ  masquĂ©es par --ignore-unfixed) et (2) risques acceptĂ©s exploitables, datĂ©s.
  • Les CVE non exploitables vivent dans .vex/openvex.json, dĂ©claration standard.
  • Les 3 gates Trivy (fs-scan + les 2 invocations image-scan) consomment --vex. Le SBOM CycloneDX (#68) reste exhaustif (inventaire, pas un gate).
  • Process rĂ©utilisable par les autres clusters (homelab, platform) : le dĂ©coupage VEX / risk acceptance est indĂ©pendant d'AWS. Suivi workspace projects/vuln-mgmt/.

CritĂšres de validation

  • [x] Scan local sans --vex : CVE-2026-48818 rĂ©apparaĂźt (preuve qu'elle n'est plus dans .trivyignore).
  • [x] Scan local avec --vex : CVE-2026-48818 supprimĂ©e, CVE-2026-54283 masquĂ©e par .trivyignore, gate fs-scan vert (exit 0).
  • [x] glab ci lint valide le pipeline modifiĂ©.
  • [ ] Pipeline MR + develop verts (gate image-scan avec --vex sur l'image rĂ©elle).

Mise Ă  jour (2026-06-30) — dette #96 levĂ©e

Les deux CVE starlette qui motivaient ces exceptions sont corrigées par le bump starlette 1.3.1 (avec fastapi 0.138 + prometheus-fastapi-instrumentator 8.0.2), proposé par Renovate (Phase 2 du vuln management) et validé par la CI : le test d'intégration tests/test_metrics.py prouve que /metrics fonctionne toujours, ce qui invalide l'hypothÚse bloquante d'origine. Un scan Trivy fs sans filtre ne détecte plus CVE-2026-48818 ni CVE-2026-54283.

En conséquence : le statement VEX CVE-2026-48818 et l'entrée .trivyignore CVE-2026-54283 sont retirés (exceptions devenues mortes) et l'issue #96 est fermée. Le pattern VEX reste cùblé (.vex/openvex.json à statements vides, flags --vex en place) pour resservir. Leçon : une remédiation se termine par le nettoyage des exceptions, sinon on laisse des angles morts qui masqueraient de futures CVE.