Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

CVE vulnerability management: from triage to patch

By updated

Improve this page

Vulnerability management turns an alert into a dated decision: detect the CVE, judge its real risk, set a fix deadline, fix or document, then verify. This lesson is for the DevOps engineer who receives scanner reports and must say, within 24 hours, what gets fixed first and what is accepted.

Everything is measured on 1 October 2026 on the nginx:1.30.5-trixie image: Trivy 0.74.0 reports 390 vulnerabilities, Grype 0.119.0 classes 18 matches as Critical where Trivy counts only one, and the flaw most likely to be exploited is rated Negligible. A raw report is therefore not handled in the order it is displayed.

What you will learn

  • Distinguish CVE, CVSS, EPSS and KEV, and know which one is authoritative for a decision
  • Run a triage workflow in five reproducible steps
  • Set fix deadlines per criticality, and document the exceptions
  • Answer "are we affected?" quickly with an SBOM and a VEX document
  • Adapt your detection to the NVD cutback since April 2026
  • Protect your own scanning tools, links in the chain like any other

What is a CVE vulnerability?

A CVE (Common Vulnerabilities and Exposures) is the public identifier assigned to a known flaw in a piece of software: CVE-2021-44228 designates Log4Shell, in the Java library Log4j. This identifier is the common reference between scanners, vendor bulletins and public databases: it lets you check that two tools are talking about the same flaw.

The volume makes manual sorting impossible. On 29 September 2026, the FIRST API assigned an EPSS score to 380,526 CVEs, and the CISA KEV catalogue listed 1,730 whose exploitation is confirmed. Reading these indicators in detail is the subject of the CVE, CVSS and EPSS lesson; this one turns them into a process, from report to fix.

Severity metrics

A CVE alone does not tell you the urgency. Three public indicators complete it, each answering a different question, and a fourth, the exposure of the component on your side, belongs to you alone. The table sums up what each indicator measures and what it does not say.

MetricWhat it measuresWhat you need to know
CVSS (Common Vulnerability Scoring System)Severity if the attack succeeds, from 0 to 10The score varies with the source: vendor, distribution or NVD
EPSS (Exploit Prediction Scoring System)Probability of exploitation within 30 daysModel v4 published since 17 March 2025, recalculated daily: 0.73 reads 73%
KEV (Known Exploited Vulnerabilities)Confirmed exploitation, maintained by CISAPresence in the catalogue = urgent handling

CVSS and EPSS do not replace each other. On the measured image, the flaw with the highest EPSS is CVE-2011-3389 (BEAST, in libgnutls30t64): 73.3%, 99th percentile, while Debian rates it Negligible. Conversely, Grype's 18 Critical matches all have an EPSS below 1% and carry the wont-fix status. FIRST sets no official threshold either: combining the indicators is up to you.

The life cycle of a vulnerability

A published CVE only concerns you if the vulnerable component is present on your side, and ideally on a code path that is actually reached. The most often forgotten step is therefore detection in your IT estate: without an up-to-date SBOM, you cannot answer quickly. The diagram follows a vulnerability from publication to closure, with the three possible outcomes of the treatment.

Life cycle of a vulnerability: the CVE is published, detected on your side by the scan and the SBOM, triaged by exposure then KEV, EPSS and CVSS, given a deadline per criticality, then fixed by an update, mitigated while waiting for the fix, or declared not exploitable through a VEX document; a new scan checks that it has gone and feeds the reporting.CVE publishedDetection on your sidescan and SBOMTriage: exposure,then KEV, EPSS, CVSSDeadline per criticality(SLA)Fix:updateMitigate while waitingfor the fixDeclare not exploitable(VEX document)New scanand reporting
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

The NVD is slowing down: what to change in your detection

The NVD (National Vulnerability Database), the US database run by NIST, changed its policy on 15 April 2026. CVE submissions grew by 263% between 2020 and 2025; NIST enriched nearly 42,000 of them in 2025, 45% more than any previous year, without keeping up.

Since then, only three categories are enriched as a priority (adding a CVSS score, CWE, CPE): CVEs in the KEV catalogue, those affecting software used by the US federal government, and those of critical software as defined by Executive Order 14028. Any non-enriched CVE published before 1 March 2026 moves to "Not Scheduled". NIST also no longer computes its own score when the CNA (the organisation that assigned the CVE) already provides one. Source: NIST announcement.

The practical consequence is twofold. A tool that only reads the NVD CVSS field may show a CVE without a score; and the displayed score increasingly depends on the chosen source. Two databases fill the gap: CISA's Vulnrichment programme and OSV.dev, an open database that aggregates advisories per ecosystem (npm, PyPI, Go, Maven). Prefer a scanner that cross-checks several sources, and compare two of them on your critical images.

Prerequisites

This lesson assumes a few notions already covered in the course, and a working pipeline. The pages below revisit them if needed.

Key concepts before you start

Three objects come back throughout the workflow: the inventory of what you ship, the declaration of what is not exploitable, and the tool that compares one with the CVE databases. Understanding them first avoids mixing them up in the pipeline.

The SBOM: your dependency inventory

An SBOM (Software Bill of Materials) is the inventory of what an artifact contains: packages, libraries, versions, licences, transitive dependencies. Without it, answering "am I exposed to this CVE?" means rescanning every application; with it, the answer is a single query on the components.

Two formats dominate: CycloneDX (OWASP, version 1.7 released in October 2025) and SPDX (Linux Foundation, version 3.0.1). Measured: Syft 1.52.0 produces CycloneDX 1.7 by default, and lists 152 packages in the nginx image, plus 3,236 files. Check which version the rest of your chain consumes before mandating one.

VEX: saying what is not exploitable

A VEX (Vulnerability Exploitability eXchange) is a document that declares, for a given CVE and product, a status: not_affected, affected, fixed or under_investigation. A not_affected must carry a justification, for example "the vulnerable code is not on an executed path".

The benefit is measurable: with an OpenVEX document declaring CVE-2011-3389 not exploitable in the nginx image, trivy image --vex goes from 390 to 389 vulnerabilities. The decision is written once, versioned, and the scanner applies it on every run instead of having the same alert reanalysed.

SCA: automated dependency scanning

SCA (Software Composition Analysis) compares the components of a project or an image with the CVE databases (NVD, OSV.dev, GitHub Advisory Database, distribution advisories). It is the first detection tool. Since the NVD cutback, no single database is enough, and the tools do not read the same ones: this explains the gaps measured below between Trivy and Grype.

The CVE vulnerability management workflow, in 5 steps

The workflow has five steps, always in the same order: detect, triage, set the deadline, treat, verify. Each one leaves a trace (report, ticket, VEX), which lets you prove afterwards that an alert was indeed handled.

Step 1, Detect: wiring up the scanning tools

Detection must be automatic and cover three families of assets: images at build time and at regular intervals, including those already in production; application dependencies on every merge request; system packages on servers. A new CVE can hit an image published three months ago: the scheduled scan (cron) on the registry matters as much as the build-time scan.

ToolWhat it scansIntegration
TrivyImages, file system, repositories, IaCCLI, image, GitHub and GitLab
Grype (in French)Images, SBOMCLI, image
OSV-Scanner (in French)Multi-ecosystem dependencies via OSV.devCLI, GitHub Actions
Dependabot (in French)Dependencies of a GitHub repositoryNative to GitHub
Renovate (in French)Dependencies, with automatic merge requestsMulti-platform

The SBOM is generated at build time, then rescanned without rebuilding the image. The two commands below were replayed on the nginx image: Syft writes the inventory, Grype checks it against its database, and --fail-on critical makes the command fail with code 2.

Terminal window
# CycloneDX inventory of the image, pinned by digest
syft nginx:1.30.5-trixie@sha256:b972f831f200b19ef0767938224f9711e74cd783718738cd7405d5cabf75c442 \
-o cyclonedx-json=sbom.json
# Scan the SBOM, without the image
grype sbom:sbom.json --fail-on critical
echo $?
# 2

Step 2, Triage: assessing the real impact

Triage answers three questions, in this order: is the component exposed (internet, internal, test)? What do the KEV, EPSS and CVSS indicators say, in this order of weight? What would the business impact of an exploitation be (data leak, downtime, privilege escalation)?

The trap is to triage on the displayed severity alone. Measured on the same image, the same flaw CVE-2026-19931 in curl is rated MEDIUM by Trivy and Critical by Grype, which uses the NVD score. The status differs too: affected for Trivy, wont-fix for Grype. For triage, Grype can sort by probability of exploitation:

Terminal window
grype sbom:sbom.json --sort-by epss
NAME INSTALLED FIXED IN TYPE VULNERABILITY SEVERITY EPSS RISK
libgnutls30t64 3.8.9-3+deb13u4 deb CVE-2011-3389 Negligible 73.3% (99th) 3.7
nginx 1.30.5-1~trixie deb CVE-2009-4487 Negligible 29.5% (98th) 1.5
libldap2 2.6.10+dfsg-1 deb CVE-2017-17740 Negligible 7.0% (93rd) 0.4

None of these CVEs is in the KEV catalogue as of 30 September 2026. The following matrix takes up the starting policy of the CVE lesson and attaches a deadline to it. The thresholds are common benchmarks, not a FIRST standard: adapt them to your exposure.

SituationIndicatorsPriority
Confirmed exploitationlisted in KEVCritical
Severe and probably exploitedCVSS ≥ 9 and high EPSSCritical
Severe, rarely exploitedCVSS ≥ 9, low EPSSHigh
Medium but heavily exploitedCVSS 4 to 6, high EPSSHigh
Low and rarely exploitedCVSS < 4, low EPSSLow: schedule or accept

Step 3, Applying the SLAs

A security SLA (Service Level Agreement) is an internal commitment on the maximum fix time, per priority level. Without it, CVEs pile up and every trade-off is replayed in a meeting. The values below are a starting point, the same as in the lesson on security KPIs:

PriorityMaximum timeEscalation if exceeded
Critical24 hoursCISO and CTO notified
High7 daysSecurity manager and lead dev
Medium30 daysTicket followed in sprint review
Low90 daysSecurity backlog, or documented acceptance

Step 4, Fix: patch, mitigation or exception

There are three outcomes, in this order of preference. Fixing updates the dependency or the base image, through the pipeline and never by hand. Mitigating reduces exposure while waiting for a fix: disable the feature, filter with a WAF (web application firewall), isolate the component. Declaring a flaw not exploitable goes through a justified VEX document.

The two Trivy commands below separate what can be fixed from what has to wait. Measured on the nginx image: the blocking scan reports 80 HIGH and CRITICAL vulnerabilities; with --ignore-unfixed, only 13 remain, all HIGH, which have a published fix. The exit code stays 1 in both cases.

Terminal window
# Blocking: any HIGH or CRITICAL fails
trivy image --severity CRITICAL,HIGH --exit-code 1 \
nginx:1.30.5-trixie@sha256:b972f831f200b19ef0767938224f9711e74cd783718738cd7405d5cabf75c442
# Total: 80 (HIGH: 79, CRITICAL: 1)
# Only what has a published fix
trivy image --severity CRITICAL,HIGH --ignore-unfixed --exit-code 1 \
nginx:1.30.5-trixie@sha256:b972f831f200b19ef0767938224f9711e74cd783718738cd7405d5cabf75c442
# Total: 13 (HIGH: 13, CRITICAL: 0)

On the Grype side, the equivalent is --only-fixed: with it, --fail-on critical exits with 0, because none of the 18 Critical CVEs has a fix. A wont-fix CVE will not go away on its own: the only way out is to change the base image, or to document it.

Step 5, Verify and report

After the fix, rescan the shipped artifact and check that the CVE has gone: it is trivial and often forgotten. A monthly or per-sprint report then gives the trend. It covers the number of CVEs detected, triaged and fixed, the SLA compliance rate, the exceptions and their due dates.

These figures feed the security KPIs, which measure the posture over time. A workflow without reporting may work, but nobody can prove it.

Integrating SBOM and VEX into your pipeline

A mature pipeline produces the SBOM and the VEX as artifacts in their own right, alongside the image. Each build thus leaves its own evidence, and the question "are we affected by this new CVE?" is settled in a few minutes, by a query on the archived SBOMs, instead of several days.

A complete GitLab CI pipeline

The official Syft and Grype images contain no shell: GitLab cannot run a script: in them. Measured: sh cannot be found in anchore/syft:v1.52.0. You need their -debug variants, which embed BusyBox, with entrypoint: [""] and the absolute path of the binary.

stages:
- scan
generate-sbom:
stage: scan
image:
name: anchore/syft:v1.52.0-debug@sha256:dd31bb351e28f958f2911dc2c53ef34340f3d61d5feb624e1ac061ccd0f87018
entrypoint: [""]
variables:
SYFT_REGISTRY_AUTH_AUTHORITY: $CI_REGISTRY
SYFT_REGISTRY_AUTH_USERNAME: $CI_REGISTRY_USER
SYFT_REGISTRY_AUTH_PASSWORD: $CI_REGISTRY_PASSWORD
script:
- /syft "registry:$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" -o cyclonedx-json=sbom.json
artifacts:
paths: [sbom.json]
scan-vulnerabilities:
stage: scan
image:
name: anchore/grype:v0.119.0-debug@sha256:166001ed93ae8463acad508c746586ced665abe9d817ae452ed9b781ddb6cebb
entrypoint: [""]
needs: [generate-sbom]
script:
# Fails (code 2) on any critical CVE that has a published fix
- /grype sbom:sbom.json --only-fixed --fail-on critical

Both scripts were replayed in these images, pinned by digest, on an image pushed to a local registry: Syft writes the SBOM, Grype exits with 0. The plumber analysis, which requires the API of a GitLab instance, could not be run. The job comment states exactly what the rule blocks: without --only-fixed, critical CVEs with no fix would block the pipeline with no action possible.

Applying a VEX to the scan

The VEX lives in the repository, next to the code, or is attached to the image as an attestation with cosign attest. Here is a minimal OpenVEX document: its justification is an example, to be established by your own analysis of the component.

vex/nginx.openvex.json
{
"@context": "https://openvex.dev/ns/v0.2.0",
"@id": "https://exemple.fr/vex/nginx-2026-10-01",
"author": "Equipe plateforme",
"timestamp": "2026-10-01T10:00:00Z",
"version": 1,
"statements": [
{
"vulnerability": { "name": "CVE-2011-3389" },
"products": [
{
"@id": "pkg:oci/nginx@sha256%3Ab972f831f200b19ef0767938224f9711e74cd783718738cd7405d5cabf75c442",
"subcomponents": [{ "@id": "pkg:deb/debian/libgnutls30t64" }]
}
],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path"
}
]
}
Terminal window
trivy image --vex vex/nginx.openvex.json \
nginx:1.30.5-trixie@sha256:b972f831f200b19ef0767938224f9711e74cd783718738cd7405d5cabf75c442
# 389 vulnerabilities instead of 390: CVE-2011-3389 no longer appears

The product is designated by the image purl, digest included: a VEX written for another version of the image does not apply. This is intended, since the justification only holds for the analysed artifact.

Troubleshooting

A creaking CVE workflow almost always shows one of the following symptoms. The table links each signal to its likely cause and to the corrective action, so you treat the cause rather than the symptom.

SymptomLikely causeSolution
Too many alerts, demotivated teamTriage on severity aloneTriage by exposure, KEV and EPSS; write VEX documents for recurring cases
The pipeline blocks with no possible actionwont-fix critical CVEs--ignore-unfixed (Trivy) or --only-fixed (Grype), and change the base image
Two scanners disagree on severityDifferent scoring sources (distribution, NVD)Set the reference source in the policy, compare on critical images
A GitLab job fails without running the scriptImage without a shell or with an imposed ENTRYPOINT-debug variant and entrypoint: [""]
CVE with no CVSS score or detailsCVE "Not Scheduled" at the NVD since April 2026Cross-check with Vulnrichment and OSV.dev
The fixed CVE comes backBase image not updatedAutomate version bumps (Renovate, Dependabot)

Key points

  • A CVE identifies a flaw; CVSS gives its severity, EPSS its probability of exploitation within 30 days, KEV confirmed exploitation.
  • You triage by exposure, then KEV, EPSS and CVSS: on nginx, the flaw with the highest EPSS (73.3%) is rated Negligible.
  • Severity depends on the source: the same CVE is MEDIUM for Trivy and Critical for Grype.
  • Deadlines per priority (24 h, 7 d, 30 d, 90 d) avoid endless trade-offs; an exception carries a review date.
  • --ignore-unfixed and --only-fixed separate what can be fixed from what requires an image change.
  • Since 15 April 2026, the NVD no longer enriches everything: cross-check with Vulnrichment and OSV.dev.
  • The SBOM answers "are we affected?", the VEX documents what is not exploitable, digest included.
  • Scanners are dependencies: minimum version, digest, security advisories followed.

Knowledge check

Check that you have grasped the essentials of this lesson. The questions only cover what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

FAQ: frequently asked questions

The questions below cover the most common doubts about vulnerability management: the difference between CVSS and EPSS, prioritisation, the role of the SBOM and the VEX, and CVEs left without a score at the NVD.

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn