Trivy’s ability to find CVEs in container images isn’t just about speed; it’s fundamentally about shifting security left by making vulnerability scanning an integrated, early-stage development tool rather than a late-stage compliance hurdle.
Let’s see Trivy in action. Imagine you’ve got a Dockerfile for a simple Node.js app:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
You’ve built this image, my-node-app:latest. Now, you want to know if it’s got any known vulnerabilities. You run Trivy:
trivy image my-node-app:latest
Here’s a snippet of what you might see:
my-node-app:latest (alpine 3.17)
Total: 110 (UNKNOWN: 0, LOW: 50, MEDIUM: 30, HIGH: 20, CRITICAL: 10)
┌──────────────────────────┬───────────────┬───────────┬───────────────┬─────────────────────────────────────────────────────────┐
│ LIBRARY │ VULNERABILITY │ SEVERITY│ INSTALLED VERSION│ FIX VERSION │
├──────────────────────────┼───────────────┼───────────┼─────────────────┼─────────────────────────────────────────────────────────┤
│ nodejs │ CVE-2023-XXXX │ CRITICAL │ 18.12.1-r0 │ 18.17.1-r0 │
│ openssl │ CVE-2023-YYYY │ HIGH │ 3.0.8-r1 │ 3.0.10-r0 │
│ alpine │ CVE-2023-ZZZZ │ HIGH │ 3.17.3-r0 │ 3.18.0-r0 │
...
This output tells you about the operating system packages (like openssl and alpine) and application dependencies (like nodejs itself, if it was installed via the OS package manager, or other libraries if you were scanning an application’s dependency files directly). Trivy checks these against its vulnerability database.
The problem Trivy solves is the inherent opacity of container images. When you docker build, you’re assembling layers of software. Each layer can contain numerous packages and libraries, each with its own version and potential security flaws. Manually tracking these versions and cross-referencing them with CVE databases is practically impossible at scale. Trivy automates this by:
- Image Layer Analysis: It inspects the filesystem of your container image, identifying installed packages and their versions. For OS packages, it understands formats like Alpine’s
.apkor Debian’s.deb. For application dependencies, it can parsepackage.json,requirements.txt,pom.xml, etc. - Vulnerability Database: It queries an extensive, regularly updated database of known vulnerabilities (CVEs) that map specific software versions to known exploits.
- Correlation: It matches the identified software and versions from your image against its database to find potential matches.
The levers you control are primarily how you invoke Trivy and what you tell it to scan. You can scan images directly:
trivy image my-node-app:latest
Or you can scan a Dockerfile before building, which is even better for shifting left:
trivy config Dockerfile
You can also target specific types of vulnerabilities or components. For instance, to only show critical and high severity CVEs:
trivy image --severity CRITICAL,HIGH my-node-app:latest
Or to scan only application dependencies in a Node.js project:
trivy fs --skip-files '/node_modules/' --scanners appdb /path/to/your/node/project
When you see a CRITICAL or HIGH vulnerability reported for a package you didn’t explicitly install (meaning it came in as a dependency of another package, or as part of the base OS image), that’s where the real value lies. It highlights the transitive nature of dependencies and the hidden risks in seemingly simple images.
Most users understand that Trivy finds CVEs in the packages they know are in their image. What’s often overlooked is Trivy’s ability to scan application-level dependencies directly from source code or lock files. For a Python project, this means running trivy fs --scanners pip on your project directory, which will analyze your requirements.txt or Pipfile.lock and find vulnerabilities in the Python packages themselves, not just the OS packages that might have been used to build your Python environment. This direct scanning of application dependencies is crucial because it catches vulnerabilities in libraries managed by pip, npm, Maven, etc., which are often independent of the underlying OS and can be a significant attack surface.
The next step after identifying vulnerabilities is integrating this into your CI/CD pipeline to automatically fail builds or alert developers.