Automated Vulnerability Scanning with Trivy

In today's fast-paced software development world, DevOps practices have become the standard for building, testing, and deploying applications efficiently. However, as teams move faster, the risk of introducing security vulnerabilities into code and infrastructure also increases. Manual security checks can't keep up with the speed of modern development cycles, often leaving gaps that attackers can exploit. That's why automating security checks directly in the CI/CD pipeline is essential. By embedding security scans into the development workflow, teams can catch vulnerabilities early, fix them quickly, and ensure that only secure code makes it to production.
In this blog post, I'll show you how to automate vulnerability scanning for a Dockerized Python application, PyGoat, using Trivy—a popular open-source security scanner. We'll use GitHub Actions to pull the PyGoat Docker image from Docker Hub and run Trivy scans as part of our CI/CD pipeline. By the end, you'll see how simple it is to add automated container security checks to your workflow without needing to build images yourself.
Prerequisites
Before we get started, make sure you have the following:
- Basic knowledge of Docker, Python, and GitHub. You don't need to be an expert, but some familiarity with how containers and GitHub repositories work will be helpful.
- A GitHub account. We'll be using GitHub Actions to automate the scanning process.
What is Trivy?
Trivy is a simple and comprehensive open-source vulnerability scanner designed for containers. It's widely used for checking Docker images for security issues before they reach production.
Trivy is especially useful because it can quickly detect a variety of vulnerabilities in your container images. It scans for known security issues in OS packages (like those installed via apt
or apk
), application dependencies (such as Python packages from pip
), and even configuration problems or exposed secrets. This broad coverage makes Trivy a valuable tool for anyone working with containerized applications, helping you catch risks early and ship safer code.
Automating Trivy Scans with GitHub Actions
GitHub Actions is a powerful CI/CD platform built into GitHub that lets you automate tasks like building, testing, and deploying your code. With GitHub Actions, you can also add automated security checks to your workflow, so vulnerabilities are caught as soon as possible.
In this example, we'll set up a workflow to automatically scan the official PyGoat Docker image from Docker Hub using Trivy every time there's a push to the repository's main branch.
Workflow
name: vulnerability-scan on: push: branches: - main jobs: trivy-scan: name: Trivy Scan runs-on: ubuntu-24.04 steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@0.28.0 with: image-ref: 'pygoat/pygoat:latest' format: 'table' exit-code: '0' ignore-unfixed: false severity: 'CRITICAL,HIGH,MEDIUM'
Let's break down what each part of this GitHub Actions workflow does:
- Trigger: The workflow runs automatically every time we push changes to the main branch.
- Job Definition: The job is named "Trivy Scan" and runs on the latest Ubuntu 24.04 virtual environment provided by GitHub.
- Checkout Step: The
actions/checkout@v4
step pulls your repository's code into the workflow environment. This is a common first step in most workflows, even though we're scanning a public Docker image and not building anything from the code. - Trivy Scan Step: The
aquasecurity/trivy-action@0.28.0
step runs the Trivy scanner. We specify several parameters to configure how Trivy operates:- image-ref:
'pygoat/pygoat:latest'
- This tells Trivy to scan thepygoat/pygoat:latest
Docker image.pygoat
is a intentionally vulnerable Python application designed for learning and practicing security testing. - format:
'table'
- The scan results will be displayed in a table format, making them easier to read in the GitHub Actions log. - exit-code:
'0'
- The workflow will not fail even if Trivy finds vulnerabilities. This is useful for demo or learning scenarios where you want to see the results without blocking the workflow. (In a production setting, you might set this to 1 so the pipeline fails if serious issues are found.) - ignore-unfixed:
true
- This option tells Trivy to ignore vulnerabilities that do not have a fix available. This is useful for focusing on issues that can be addressed immediately. - severity:
'CRITICAL'
- Limits the output to vulnerabilities with Critical severity.
- image-ref:
Results
Once the workflow runs, you'll see the Trivy scan results right in the GitHub Actions log. Here's an example snippet of what a Trivy output looks like:
To understand what these results mean, let's break down the first row in the table:
- Library:
curl
- This is the name of the library/package affected. - Vulnerability:
CVE-2021-22945
- This is the vulnerability found in the library. For more information on CVEs, you can visit the CVE website. - Severity:
CRITICAL
- This indicates the severity level of the vulnerability. - status:
fixed
- This shows that the vulnerability has been fixed in a later version of the library. - Installed Version:
7.74.0-1.3+deb11u1
- This is the version of the library currently installed in the Docker image. - Fixed Version:
7.74.0-1.3+deb11u2
- This is the version where the vulnerability has been fixed. - Title:
curl: use-after-free and double-free in MQTT sending
- This is a brief title summarizing the vulnerability.
These results help us quickly identify which libraries in our Docker image have known vulnerabilities, their severity, and whether a fix is available. We can then prioritize which issues to address first based on their impact.
Conclusion
Automating vulnerability scanning in your CI/CD pipelines is a crucial step toward building secure applications. With tools like Trivy and platforms like GitHub, adding security checks to your development workflow is both straightforward and highly effective. By scanning your Docker images every time you push code, you can catch vulnerabilities early, prioritize fixes, and ship more secure applications—without slowing down your team.
Remember, vulnerability scanning isn't a one-time task; it's an ongoing process. As new vulnerabilities are discovered, continuous scans ensure you're always aware of the latest risks in your dependencies and base images. While this tutorial focused on scanning a deliberately vulnerable demo app, the same approach applies to real-world projects of any size.