skip to content

Trivy 0.74 rates a CVE LOW on a RHEL-based image while NVD rates it HIGH — how did Trivy pick that severity, and how do you prove it?

level: seniorimportance: should knowfreq 26%

answer

  1. the matched advisory source decides
  2. the vendor knows how it built it
  3. SeveritySource and VendorSeverity
  4. CVSS bands, then other sources
  5. --vuln-severity-source reorders

basics

~20 s

Trivy takes severity from the advisory source it matched the package against: for an OS package, the distribution vendor, which rates its own build. JSON fields SeveritySource and VendorSeverity show which source won and every source's rating.

solid answer

~40 s

Severity follows the advisory. By default, for an RPM-installed package Trivy matches only the vendor's advisories and uses the vendor's rating, since the vendor knows its compile options and default configuration and NVD does not. Trivy's own documentation uses CVE-2023-0464: HIGH in NVD, Low impact from Red Hat, so Trivy shows LOW. If the chosen source has no rating, Trivy derives a band from a CVSS score, then falls back to NVD, and uses other vendors' ratings before settling on UNKNOWN. To prove it, run with `--format json` and read `SeveritySource` plus the `VendorSeverity` map, where 0 to 4 mean UNKNOWN to CRITICAL. If policy demands a fixed order, `--vuln-severity-source` (added in 0.60.0, default `auto`) sets it. Remember that `--severity` filters on whatever was chosen.

code

json · 10 lines
json
{
  "VulnerabilityID": "CVE-2023-0464",
  "PkgName": "openssl-libs",
  "Severity": "LOW",
  "SeveritySource": "redhat",
  "VendorSeverity": {
    "nvd": 3,
    "redhat": 1
  }
}

go deeper

for a junior

Recall that Trivy shows the distribution vendor's rating for OS packages, so it can differ from the NVD number you see on a CVE page.

for a middle

Explain the selection chain: matched source, CVSS band, NVD fallback, other vendors, then UNKNOWN, and where SeveritySource and VendorSeverity appear.

for a senior

Show you can settle a 'Trivy is wrong' dispute from the JSON, and explain how severity choice shifts what --severity and a gate let through on different base images.

for a principal

Weigh standardising on one severity source across scanners against keeping vendor context, and how either choice changes fleet-wide numbers and exception volume.

## Why one CVE has several severities A **CVE** identifies a flaw in upstream code. A **severity** is a judgement about impact, and different parties judge different things. NVD rates the upstream flaw without knowing how any distribution builds the package. A distribution vendor rates the package **it ships**: its compile options, its default configuration, whether the vulnerable feature is even built. Trivy's documentation states the consequence plainly: the vendor's rating is more accurate for that package, so Trivy prefers it. ## How Trivy 0.74 chooses The choice is tied to the **data source** Trivy matched the package against: 1. **OS packages** installed by `dnf`, `apt`, `apk` and the like are matched, under the default detection priority, only against that distribution's advisories, and the severity is taken from the same source. On a RHEL-based image that is Red Hat's rating. 2. **Language packages** installed by `pip`, `npm` and similar are matched against language sources such as the GitHub Advisory Database, so their severity usually comes from there. 3. If the selected source gives **no severity**, Trivy maps a CVSS base score to a band — 0.1–3.9 Low, 4.0–6.9 Medium, 7.0–8.9 High, 9.0–10.0 Critical. 4. If there is **no CVSS score** either, it falls back to NVD. 5. Because NVD and some vendors are slow to analyse, Trivy uses other vendors' ratings when NVD has none yet; only when **no source** has a rating does the finding become `UNKNOWN`. The documented example is CVE-2023-0464: NVD rates it HIGH, Red Hat marks its impact Low, and Trivy displays LOW for a Red Hat package. ## Proving it from the report The table shows only the final severity. The JSON report (`--format json`) shows the reasoning: | Field | What it tells you | |---|---| | `Severity` | the rating Trivy assigned and filtered on | | `SeveritySource` | the source that rating came from, such as `redhat`, `debian` or `ghsa` | | `VendorSeverity` | every source's rating, as numbers | | `DataSource` | the advisory the match itself came from | `VendorSeverity` uses numbers, not CVSS scores: 0 UNKNOWN, 1 LOW, 2 MEDIUM, 3 HIGH, 4 CRITICAL. A finding showing `"nvd": 3` and `"redhat": 1` with `SeveritySource` `redhat` is the whole story in two lines. ## Overriding the order Since 0.60.0, `--vuln-severity-source` takes an ordered list of source IDs; Trivy checks them in order and uses the first that has a rating, or `UNKNOWN` if none does. The default is `auto`, the logic above, and `auto` can appear in the list to fall back to it. The documentation's worked example, for an Alpine finding whose `VendorSeverity` holds `ghsa` 3 and `nvd` 4: - `auto,nvd` gives CRITICAL, from `auto`; - `alpine,ghsa` gives HIGH, from `ghsa`; - `alpine,alma` gives UNKNOWN, because neither source rated it. Forcing `nvd` first makes reports match an NVD-based policy, at the price of discarding the vendor's knowledge of its own build and of more `UNKNOWN` findings while NVD analysis lags. ## Why this matters downstream - `--severity` and any severity gate act on the **assigned** value. The same CVE can pass a HIGH-and-CRITICAL filter on one base image and be dropped on another. - A lower vendor rating is not evidence that Trivy's database is stale; check `SeveritySource` before opening a bug. - Comparing Trivy's counts with another scanner's is meaningless until you know which severity source each used. ## A worked investigation When a team reports that Trivy "downgraded" a CVE, settle it in a few minutes: 1. Re-run the scan with `--format json` against the same image and note the database's `UpdatedAt` from `trivy version --format json`, so both sides compare the same data. 2. Find the finding by `VulnerabilityID` and package, and read `Severity` and `SeveritySource` together. 3. Read `VendorSeverity`: if `nvd` is 3 and the vendor's entry is 1, the difference is the vendor's assessment, not a defect. 4. Check `DataSource` and the package's origin. An OS package and a `pip`-installed copy of the same library can legitimately carry different ratings in one report. 5. If the organisation's policy requires another source, change it once with `--vuln-severity-source` in shared configuration, rather than per-team exceptions. ## Scope What the severity should mean for a remediation queue — exploit likelihood, known exploitation — is a separate prioritisation question; this question is only about which rating Trivy picks and why.

  • Why can a Python package in the same image show SeveritySource ghsa instead of redhat?
    Source selection follows how the package was installed. A package installed by the OS package manager is matched only against the vendor's advisories; one installed by `pip` is matched against language sources such as the GitHub Advisory Database, so its severity comes from that source.
  • Policy says 'use NVD severity'. What do you configure, and what do you lose?
    Set `--vuln-severity-source nvd,auto` so NVD wins when it has a rating and the default logic covers the rest; `nvd` alone yields UNKNOWN wherever NVD has not rated. You lose the vendor's knowledge of its own build, so findings the vendor rated Low on a hardened package come back as HIGH.

saying these in an interview costs you the question

  • Trivy always uses the NVD CVSS score as a finding's severity
  • A LOW where NVD says HIGH means Trivy's database is out of date
  • The numbers in VendorSeverity are CVSS base scores
  • --severity chooses which source Trivy takes the rating from
  • A vendor rating below NVD's is just downplaying, so NVD should always win