Back to articles
3 min read

Scan your Docker images for CVEs, it takes 10 seconds

Trivy finds vulnerabilities in your containers. Almost nobody runs it in CI/CD.

DockerSecurityCI/CDDevEx

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.

Comments