Skip to content

Trivy

What Is It?

Trivy is an open-source vulnerability scanner that inspects container images, filesystems, and running systems for known security issues. Given an image name, it identifies the operating system, lists installed OS packages and language-level dependencies (pip, npm, gem, …), looks each one up in a vulnerability database, and reports the CVEs that affect them.

Trivy is the automated half of container image audit. It will reliably find publicly known vulnerabilities — outdated openssl, vulnerable npm dependencies, expired packages — but it can do nothing about a custom-written backdoor a malicious image author has planted, because no public CVE describes it. Pair it with Dive for layer-level manual inspection.

Installation

Trivy is distributed via an official RPM repository for RHEL/CentOS:

sudo tee /etc/yum.repos.d/trivy.repo > /dev/null <<'EOF'
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://aquasecurity.github.io/trivy-repo/rpm/public.key
EOF

sudo dnf install -y trivy

Verify:

trivy --version

The first scan will download the vulnerability database (~50–100 MB). Subsequent scans use the cached DB and only refresh it every 12 hours.

Key Files and Directories

Path Purpose
/etc/yum.repos.d/trivy.repo RPM repository definition
~/.cache/trivy/ Cached vulnerability database
/usr/bin/trivy The Trivy binary

Configuration

Trivy is configured primarily via command-line flags. For most lab work, no config file is needed. The behaviour you'll most often tune:

Severity Filter

By default, Trivy reports vulnerabilities at every severity level (LOW, MEDIUM, HIGH, CRITICAL, UNKNOWN). On a deliberately old image this is overwhelming. Filter:

trivy image --severity CRITICAL <image>
trivy image --severity CRITICAL,HIGH <image>

Output Format

table (the default) is human-readable. json is machine-readable and useful when piping into other tools:

trivy image --format json <image>            # all data, structured
trivy image --format json --severity CRITICAL <image> | jq '.Results[].Vulnerabilities[].VulnerabilityID' | sort -u

Disabling Secret Scanning

Trivy also looks for accidentally-committed secrets (API keys, passwords). On most images this is empty and slow:

trivy image --scanners vuln <image>

Common Commands

# Standard scan
trivy image registry.hpc.ut.ee/public/lab10-consultancy:latest

# Critical-only, no progress bar (good for scripting)
trivy image --severity CRITICAL --no-progress \
  registry.hpc.ut.ee/public/lab10-consultancy:latest

# JSON output, only vulnerabilities, list of CVE IDs
trivy image --severity CRITICAL --scanners vuln --format json <image> \
  | jq -r '.Results[].Vulnerabilities[]?.VulnerabilityID' | sort -u

# Refresh the vulnerability DB (normally automatic every 12h)
trivy image --download-db-only

# Scan a filesystem path instead of an image
trivy fs /path/to/project

Reading the Output

A typical CRITICAL-severity table looks like this:

registry.hpc.ut.ee/public/lab10-consultancy:latest (alpine 3.11.11)

Total: 4 (CRITICAL: 4)

┌──────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬──────────────────────────────────┐
│   Library    │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │             Title                │
├──────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼──────────────────────────────────┤
│ apk-tools    │ CVE-2021-36159 │ CRITICAL │ fixed  │ 2.10.6-r0         │ 2.10.7-r0     │ libfetch out of boundary read    │
│ libcrypto1.1 │ CVE-2021-3711  │ CRITICAL │ fixed  │ 1.1.1k-r0         │ 1.1.1l-r0     │ openssl: SM2 buffer overflow     │
│ zlib         │ CVE-2022-37434 │ CRITICAL │ fixed  │ 1.2.11-r3         │ 1.2.11-r4     │ zlib: heap buffer over-read      │
└──────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴──────────────────────────────────┘

What each column means:

  • Library — the affected package or module
  • Vulnerability — the CVE-ID (CVE-YYYY-NNNNN format). This is the canonical identifier you record or look up.
  • Severity — Trivy's classification, derived from the upstream CVE's CVSS score.
  • Statusfixed means a patched version exists; affected means the vulnerability is unpatched.
  • Installed / Fixed Version — what's in the image vs. what would resolve the CVE.

For each CRITICAL CVE, the question is usually: can I rebuild the image on a newer base, or upgrade the offending package?

Logging and Debugging

Trivy logs to stderr at INFO level by default. Useful flags:

  • --debug — verbose output for troubleshooting why a scan failed
  • --quiet — suppress informational messages, only print results
  • --cache-dir <path> — override the cache directory if disk space is tight in ~

Common errors:

  • unable to download db: check internet connectivity and that ghcr.io is reachable.
  • OS version is no longer supported by the distribution: expected on deliberately old images. Trivy still reports CVEs, just with a warning that the upstream may not be patching them anymore.
  • no such image: the image must be pulled first, or the registry path must be reachable.

Security Considerations

  • Trivy's vulnerability database comes from upstream and is updated regularly. A CVE that wasn't reported last week may appear today.
  • "No CRITICAL CVEs" is not a clean bill of health. Trivy can only find what's in its database. Custom-planted malware does not have a CVE-ID and will pass Trivy with no findings. Always pair Trivy with manual inspection (Dive).
  • Trivy can also scan filesystems (trivy fs), git repositories (trivy repo), and Kubernetes manifests (trivy k8s). All can be useful as CI gates.

Further Reading