Scan your Docker images for CVEs, it takes 10 seconds
Trivy finds vulnerabilities in your containers. Almost nobody runs it in CI/CD.
Your Docker images have vulnerabilities. Critical ones. You’re shipping them to production every day.
Trivy scans images for CVEs in under 10 seconds. Adding it to CI/CD takes 5 lines of YAML. I’ve reviewed hundreds of pipelines and almost none of them scan for vulnerabilities.
Install and run
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
trivy image python:3.4-alpine First scan downloads the vulnerability database. Takes a few seconds. Every scan after that runs instantly.
Output shows CVE IDs, severity, affected packages, and fixed versions:
python:3.4-alpine (alpine 3.4.6)
==================================
Total: 37 (UNKNOWN: 1, LOW: 15, MEDIUM: 11, HIGH: 9, CRITICAL: 1)
┌──────────────┬────────────────┬──────────┬───────────────────┬───────────────┬────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
├──────────────┼────────────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────┤
│ curl │ CVE-2018-14618 │ CRITICAL │ 7.61.0-r0 │ 7.61.1-r0 │ curl: NTLM password overflow via integer │
│ │ │ │ │ │ overflow │
├──────────────┼────────────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────┤
│ libcurl │ CVE-2018-14618 │ CRITICAL │ 7.61.0-r0 │ 7.61.1-r0 │ curl: NTLM password overflow via integer │
│ │ │ │ │ │ overflow │
├──────────────┼────────────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────┤
│ openssl │ CVE-2021-23840 │ HIGH │ 1.0.2p-r0 │ 1.0.2u-r0 │ openssl: integer overflow in CipherUpdate │
├──────────────┼────────────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────┤
│ zlib │ CVE-2018-25032 │ HIGH │ 1.2.11-r0 │ 1.2.12-r0 │ zlib: A flaw found in zlib when compressing│
└──────────────┴────────────────┴──────────┴───────────────────┴───────────────┴────────────────────────────────────────────┘ You know exactly what’s broken and which version fixes it. The table shows the library, CVE ID, severity level, current version, and the version that patches the vulnerability.
Fail CI on critical CVEs
# GitHub Actions
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
severity: 'CRITICAL,HIGH'
exit-code: '1' # GitLab CI
trivy_scan:
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:latest Pipeline fails if critical or high vulnerabilities exist. Forces you to fix them before deploying.
Ignore unfixed vulnerabilities
Some CVEs have no fix available yet. Trivy can ignore them:
trivy image --ignore-unfixed myapp:latest Only shows vulnerabilities you can actually fix.
Scan takes seconds
Small images scan in under 5 seconds. Large images with Java dependencies take 10-30 seconds.
First scan downloads the database (50MB). Subsequent scans use the cached database.
If scans timeout on huge images, increase the limit:
trivy image --timeout 15m huge-java-app:latest Or reduce parallelism to save disk space:
trivy image --parallel 1 myapp:latest Why nobody does this
I’ve seen teams deploy images with 47 critical CVEs. They had no idea. Nobody checked.
Adding Trivy to CI/CD takes 5 minutes. Running it adds 10 seconds to build time. The barrier isn’t technical.
It’s organizational. Security feels like someone else’s problem until you get breached. By then it’s too late.
It catches real issues
Last month I scanned a Python image we’d been using for 6 months. Found 15 critical vulnerabilities in base OS packages. Updated to a newer base image, rescanned, down to 2 unfixed CVEs.
Took 20 minutes total. We’d been shipping those vulnerabilities to production daily.
Output formats
JSON for parsing in scripts:
trivy image --format json -o results.json myapp:latest SARIF for GitHub security tab:
- uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
- uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif' Results show up in GitHub’s security dashboard.
Beyond containers
Trivy scans more than images:
# Filesystem
trivy fs /path/to/project
# Git repository
trivy repo https://github.com/user/project
# Kubernetes cluster
trivy k8s --report summary
# Infrastructure as code
trivy config ./terraform Same tool, different targets. One scanner for everything.
Start now
Add this to your GitHub Actions:
name: Scan
on: [push]
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1 Or GitLab CI:
stages:
- build
- scan
build:
stage: build
script:
- docker build -t myapp:latest .
scan:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:latest Five lines of config. Ten seconds per scan. No excuses.
Bottom line
Vulnerabilities exist in every image. Trivy finds them before they reach production.
The tooling is free, fast, and trivial to integrate. The only reason not to use it is you haven’t prioritized security yet.
Add Trivy to your pipeline and congrats, you’re no longer a DevOps engineer. You’re a DevSecOps engineer now. Go update your LinkedIn.
Fix that today.
Reality is often more nuanced. But me? Nuance bores me. I'd rather be clear.