Trivy: The All-in-One Security Scanner for Your CI/CD Pipeline
If you're building Docker images and pushing them to production, you need to know what vulnerabilities are hiding inside. Trivy is a free, open-source security scanner from Aqua Security that has become the de facto standard for container security scanning.
What Trivy Actually Does
Trivy scans multiple targets for security issues:
- Container images – find CVEs in your Docker images
- Filesystems – scan your local codebase
- Git repositories – audit remote repos directly
- Kubernetes clusters – check your entire K8s deployment
- IaC files – detect misconfigurations in Terraform, CloudFormation, etc.
Beyond vulnerabilities, Trivy also detects exposed secrets (API keys, passwords), license risks, and generates SBOMs (Software Bill of Materials).
Quick Start
Install Trivy and scan an image in seconds:
# Install on Ubuntu/Debian
sudo apt-get install trivy
# Or via the install script
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Scan a Docker image
trivy image nginx:latest
The output shows vulnerabilities grouped by severity (CRITICAL, HIGH, MEDIUM, LOW) with CVE identifiers and fix recommendations.
GitHub Actions Integration
Here's a minimal workflow that scans your Docker image on every push:
name: Security Scan
on:
push:
branches: [main]
pull_request:
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
severity: 'CRITICAL,HIGH'
exit-code: '1'
Setting exit-code: '1' fails the pipeline when critical or high vulnerabilities are found – exactly what you want for a security gate.
Scanning Your Codebase
You can also scan your repository for vulnerabilities in dependencies:
# Scan current directory
trivy filesystem .
# Scan with specific scanners
trivy filesystem --scanners vuln,secret,misconfig .
This catches vulnerable npm packages, Python dependencies, exposed .env files, and misconfigured Dockerfiles before they reach production.
Test It Yourself: Secret Detection in Action
Before trusting any security tool in production, you should verify it actually works. A simple way to test Trivy's secret detection is to add a file with fake secrets to your repository and run a scan.
Create a file called test-secrets.js in your repo:
// This file contains secrets that should be detected
const config = {
// NPM token - should trigger detection
npmToken: 'npm_ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
// OpenAI API key - should trigger detection
openaiApiKey: 'sk-ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789012345678901234',
// GitHub token - should trigger detection
githubToken: 'ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
// AWS access key - should trigger detection
awsAccessKey: 'AKIA1234567890ABCD',
// Stripe API key - should trigger detection
stripeKey: 'sk_live_ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890',
// Google API key - should trigger detection
googleApiKey: 'AIzaSyABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
};
// Environment variables - should trigger detection
process.env.API_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
process.env.SECRET_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
// JWT secret - should trigger detection
const jwtSecret = 'JWT_SECRET=ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789012345678901234567890';
// OAuth token - should trigger detection
const oauthToken = 'oauth_token:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789012345678901234567890';
Now run Trivy with secret scanning enabled and a custom configuration:
trivy fs --scanners secret --secret-config trivy-secret.yaml .
You'll get a detailed report showing exactly what was found:
Report Summary
┌───────────┬──────┬─────────┐
│ Target │ Type │ Secrets │
├───────────┼──────┼─────────┤
│ config.js │ text │ 11 │
└───────────┴──────┴─────────┘
config.js (secrets)
Total: 11 (HIGH: 0, CRITICAL: 11)
CRITICAL: GitHub (github-pat)
══════════════════════════════════════════════════════════════════════════════
GitHub Personal Access Token
──────────────────────────────────────────────────────────────────────────────
config.js:10 (offset: 358 bytes)
──────────────────────────────────────────────────────────────────────────────
8
9 // GitHub token - should trigger detection
10 [ githubToken: '****************************************',
11
──────────────────────────────────────────────────────────────────────────────
CRITICAL: general (openai-api-key)
══════════════════════════════════════════════════════════════════════════════
OpenAI API Key
──────────────────────────────────────────────────────────────────────────────
config.js:7 (offset: 238 bytes)
──────────────────────────────────────────────────────────────────────────────
5
6 // OpenAI API key - should trigger detection
7 [ openaiApiKey: '**************************************************34',
8
──────────────────────────────────────────────────────────────────────────────
CRITICAL: general (stripe-api-key)
══════════════════════════════════════════════════════════════════════════════
Stripe API Key
──────────────────────────────────────────────────────────────────────────────
config.js:16 (offset: 553 bytes)
──────────────────────────────────────────────────────────────────────────────
14
15 // Stripe API key - should trigger detection
16 [ stripeKey: '********************************************',
17
──────────────────────────────────────────────────────────────────────────────
... and 8 more findings (NPM tokens, JWT secrets, OAuth tokens, etc.)
Trivy flags 11 critical findings – NPM tokens, GitHub PATs, OpenAI keys, Stripe keys, JWT secrets, and more. If your CI pipeline catches these test secrets, you know it will catch real ones too.
Custom Secret Detection Rules
For more control over what Trivy detects, create a trivy-secret.yaml configuration file:
# trivy-secret.yaml
# Custom rules for secret detection (Frontend/Node.js/Angular specific)
rules:
# Node.js / npm specific
- id: npm-token
category: general
title: NPM Token
severity: CRITICAL
regex: 'npm_[A-Za-z0-9]{36}'
- id: npm-auth-token
category: general
title: NPM Auth Token
severity: CRITICAL
regex: '//registry\.npmjs\.org/:_authToken\s*=\s*[A-Za-z0-9+/=]{40,}'
# API Keys
- id: openai-api-key
category: general
title: OpenAI API Key
severity: CRITICAL
regex: 'sk-[A-Za-z0-9]{48}'
- id: google-api-key
category: general
title: Google API Key
severity: CRITICAL
regex: 'AIza[0-9A-Za-z_-]{35}'
- id: stripe-api-key
category: general
title: Stripe API Key
severity: CRITICAL
regex: 'sk_live_[A-Za-z0-9]{24,}'
- id: stripe-publishable-key
category: general
title: Stripe Publishable Key
severity: HIGH
regex: 'pk_live_[A-Za-z0-9]{24,}'
# OAuth and JWT tokens
- id: oauth-token
category: general
title: OAuth Token
severity: CRITICAL
regex: 'oauth_token\s*[:=]\s*[A-Za-z0-9_-]{32,}'
- id: jwt-secret
category: general
title: JWT Secret
severity: CRITICAL
regex: 'JWT_SECRET\s*[:=]\s*[A-Za-z0-9+/=]{32,}'
# Environment variables with secrets
- id: env-secret
category: general
title: Environment Secret
severity: HIGH
regex: '(API_KEY|SECRET_KEY|PRIVATE_KEY|ACCESS_TOKEN|REFRESH_TOKEN)\s*[:=]\s*[A-Za-z0-9+/=_-]{20,}'
# AWS credentials
- id: aws-access-key
category: general
title: AWS Access Key ID
severity: CRITICAL
regex: 'AKIA[0-9A-Z]{16}'
- id: aws-secret-key
category: general
title: AWS Secret Access Key
severity: CRITICAL
regex: 'aws_secret_access_key\s*[:=]\s*[A-Za-z0-9/+=]{40}'
# GitHub tokens
- id: github-token
category: general
title: GitHub Personal Access Token
severity: CRITICAL
regex: 'ghp_[A-Za-z0-9]{36}'
- id: github-oauth
category: general
title: GitHub OAuth Token
severity: CRITICAL
regex: 'gho_[A-Za-z0-9]{36}'
allow-rules:
# Allow example/placeholder values
- id: example-values
description: Allow example placeholder values
regex: '(example|placeholder|your-.*-here|xxx+|CHANGE_ME|test|demo)'
# Allow .env.example file
- id: env-example
description: Allow .env.example file
path: '\.env\.example$'
# Allow test files
- id: test-files
description: Allow test files
path: '(\.spec\.|\.test\.|/test/|/tests/)'
# Allow documentation files
- id: documentation
description: Allow documentation files
path: '(README|\.md|\.txt)$'
This configuration defines custom regex patterns for common secret types and includes allow-rules to reduce false positives in test files, documentation, and placeholder values.
Important: Remember to delete the test secrets file after verification, or add it to .trivyignore if you want to keep it as a permanent test fixture.
Why Trivy?
After evaluating several vulnerability scanners, Trivy stands out for a few reasons:
- Zero configuration – it just works out of the box
- Fast – scans complete in seconds, not minutes
- No external dependencies – single binary, no database server needed
- Wide ecosystem support – integrates with Harbor, GitLab, Azure, and more
- Apache 2.0 license – fully open source, no vendor lock-in
The tool is actively maintained and trusted by companies like GitLab, MasterCard, and Deutsche Bahn.
Wrapping Up
Adding Trivy to your CI/CD pipeline takes 5 minutes and gives you continuous visibility into your security posture. For any team shipping containers, it's an essential tool that should be part of every build.
Check out the official documentation at trivy.dev and the GitHub repository at aquasecurity/trivy.








