Install any skill in seconds. Free to start, no credit card required.
Get Started Free →This skill covers integrating Aqua Security's Trivy scanner into CI/CD pipelines for comprehensive container image vulnerability detection. It addresses scanning Docker images for OS package and application dependency CVEs, detecting misconfigurations in Dockerfiles, scanning filesystem and git repositories, and establishing severity-based quality gates that block deployment of vulnerable images.
.claude/skills/scanning-containers-with-trivy-in-cicd/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 10 |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-18 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-15 | ✓→✓ | = Same ✓ | — | — |
| case-09 | ✓→✓ | = Same ✓ | — | — |
| case-02 | ✗→✗ | = Same ✗ | — | — |
Do not use for runtime container security monitoring (use Falco), for scanning running containers in production (use runtime agents), or when only scanning application source code without containerization (use SAST tools).
Set up a GitHub Actions workflow that builds a Docker image and scans it with Trivy before pushing to a container registry.
yaml# .github/workflows/container-security.yml name: Container Security Scan on: push: branches: [main] pull_request: branches: [main] paths: - 'Dockerfile' - 'docker-compose*.yml' - 'src/**' - 'requirements*.txt' - 'package*.json' jobs: build-and-scan: runs-on: ubuntu-latest permissions: security-events: write contents: read steps: - uses: actions/checkout@v4 - name: Build Docker image run: docker build -t app:${{ github.sha }} . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@0.28.0 with: image-ref: 'app:${{ github.sha }}' format: 'sarif' output: 'trivy-results.sarif' severity: 'CRITICAL,HIGH' exit-code: '1' ignore-unfixed: true - name: Upload Trivy scan results uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: 'trivy-results.sarif' category: 'trivy-container' - name: Run Trivy misconfiguration scanner uses: aquasecurity/trivy-action@0.28.0 with: scan-type: 'config' scan-ref: '.' format: 'table' exit-code: '1' severity: 'CRITICAL,HIGH'
Trivy detects common Dockerfile security issues such as running as root, using latest tags, and exposing unnecessary ports.
bash# Scan Dockerfile for misconfigurations trivy config --severity HIGH,CRITICAL ./Dockerfile # Scan with custom policy directory trivy config --policy ./security-policies --severity MEDIUM,HIGH,CRITICAL . # Example secure Dockerfile practices Trivy checks for: # - USER instruction present (not running as root) # - HEALTHCHECK instruction defined # - Base image uses specific tag, not :latest # - No secrets in ENV or ARG instructions # - COPY preferred over ADD
yaml# .gitlab-ci.yml stages: - build - scan - push variables: TRIVY_CACHE_DIR: .trivycache/ build: stage: build script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . - docker save $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -o image.tar artifacts: paths: - image.tar trivy-scan: stage: scan image: name: aquasec/trivy:latest entrypoint: [""] cache: paths: - .trivycache/ script: - trivy image --input image.tar --exit-code 1 --severity CRITICAL,HIGH --ignore-unfixed --format json --output trivy-report.json - trivy image --input image.tar --severity CRITICAL,HIGH,MEDIUM --format table artifacts: reports: container_scanning: trivy-report.json paths: - trivy-report.json allow_failure: false push: stage: push needs: [trivy-scan] script: - docker load -i image.tar - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
Manage false positives and accepted risks through Trivy's ignore file and VEX statements.
yaml# .trivyignore.yaml vulnerabilities: - id: CVE-2023-44487 # HTTP/2 rapid reset - mitigated at load balancer statement: "Mitigated by WAF rate limiting at ingress layer" expires: 2026-06-01 - id: CVE-2024-21626 # runc container escape - patched in base image update statement: "Tracked in JIRA-SEC-1234, base image update scheduled" expires: 2026-03-15 misconfigurations: - id: DS002 # User not set - required for init containers paths: - "docker/init-container/Dockerfile" statement: "Init container requires root for volume permission setup"
Cache the Trivy vulnerability database in CI/CD to reduce scan times and enable air-gapped environments.
yaml# GitHub Actions with database caching - name: Cache Trivy DB uses: actions/cache@v4 with: path: /tmp/trivy-db key: trivy-db-${{ hashFiles('.github/workflows/container-security.yml') }} restore-keys: trivy-db- - name: Run Trivy with cached DB uses: aquasecurity/trivy-action@0.28.0 with: image-ref: 'app:${{ github.sha }}' cache-dir: /tmp/trivy-db format: 'json' output: 'trivy-results.json' severity: 'CRITICAL,HIGH' exit-code: '1'
bash# Air-gapped: Download DB manually and mount trivy image --download-db-only --cache-dir /path/to/cache # Transfer cache to air-gapped system trivy image --skip-db-update --cache-dir /path/to/cache myimage:tag
Use Trivy to generate Software Bill of Materials alongside vulnerability scanning.
bash# Generate SBOM in CycloneDX format trivy image --format cyclonedx --output sbom.cdx.json app:latest # Generate SBOM in SPDX format trivy image --format spdx-json --output sbom.spdx.json app:latest # Scan SBOM for vulnerabilities (decouple generation from scanning) trivy sbom sbom.cdx.json --severity CRITICAL,HIGH # Scan with license detection trivy image --scanners vuln,license --severity HIGH,CRITICAL app:latest
| Term | Definition | |------|------------| | CVE | Common Vulnerabilities and Exposures — standardized identifiers for publicly known security vulnerabilities | | Vulnerability DB | Trivy's regularly updated database aggregating CVE data from NVD, vendor advisories, and language-specific sources | | Misconfiguration | Security-relevant configuration issue in Dockerfiles, Kubernetes manifests, or IaC templates | | SBOM | Software Bill of Materials — complete inventory of all components and dependencies in a container image | | Ignore Unfixed | Flag to skip CVEs without available patches, reducing noise from vulnerabilities with no actionable fix | | VEX | Vulnerability Exploitability eXchange — machine-readable statements about whether a vulnerability is exploitable in context | | Exit Code | Non-zero return code from Trivy when findings exceed the severity threshold, used to fail CI/CD pipelines |
Context: A team builds multi-stage Docker images and needs to scan the final production image before pushing to ECR, while also scanning the build stage for supply chain risks.
Approach:
--target production for the final stage--severity CRITICAL,HIGH --exit-code 1 --ignore-unfixed to block on exploitable issuesPitfalls: Scanning only the final stage misses vulnerable packages that were present in build stages and may have influenced the build. Run trivy fs on the build context separately. Caching the Trivy DB too aggressively (weekly) means newly published CVEs take days to appear in scans.
Trivy Container Scan Report
=============================
Image: app:a1b2c3d4
Base Image: python:3.12-slim-bookworm
Scan Date: 2026-02-23
DB Version: 2026-02-23T00:15:00Z
VULNERABILITY SUMMARY:
Total: 47
Critical: 2
High: 5
Medium: 18
Low: 22
Unfixed: 8 (excluded from gate)
CRITICAL FINDINGS:
CVE-2025-12345 libssl3 3.0.11-1 3.0.13-1 OpenSSL buffer overflow
CVE-2025-67890 curl 7.88.1-10 7.88.1-12 curl HSTS bypass
HIGH FINDINGS:
CVE-2025-11111 zlib1g 1.2.13 1.2.13.1 zlib heap buffer overflow
CVE-2025-22222 python3.12 3.12.1 3.12.3 CPython path traversal
CVE-2025-33333 requests 2.31.0 2.32.0 requests SSRF in redirects
MISCONFIGURATION:
DS002 [HIGH] Dockerfile: USER instruction not set (running as root)
DS026 [MEDIUM] Dockerfile: No HEALTHCHECK defined
QUALITY GATE: FAILED (2 Critical, 5 High findings)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.