Skip to content

Trivy Cheat Sheet

Overview

Trivy is a comprehensive security scanner designed to find vulnerabilities, misconfigurations, secrets, SBOM (Software Bill of Materials), and license issues in containers, Kubernetes, code repositories, clouds, and more. Developed by Aqua Security, Trivy is fast, accurate, and easy to use, making it an essential tool for DevSecOps pipelines.

Key Features

  • Multi-Target Scanning: Container images, filesystems, repositories, Kubernetes clusters
  • Vulnerability Detection: OS packages, language-specific packages, known vulnerabilities
  • Misconfiguration Detection: Infrastructure as Code (IaC) security issues
  • Secret Detection: API keys, passwords, tokens in code and images
  • SBOM Generation: Software Bill of Materials in multiple formats
  • License Compliance: License detection and compliance checking
  • CI/CD Integration: Native integration with popular CI/CD platforms
  • Multiple Output Formats: JSON, SARIF, CycloneDX, SPDX, and more

Installation

bash
# Linux (x86_64)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.48.0

# Linux (ARM64)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.48.0

# macOS (Homebrew)
brew install trivy

# Windows (Chocolatey)
choco install trivy

# Windows (Scoop)
scoop install trivy

# Verify installation
trivy version

Docker Installation

bash
# Pull Trivy image
docker pull aquasec/trivy:latest

# Create alias for easy usage
alias trivy="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/Library/Caches:/root/.cache/ aquasec/trivy:latest"

# Scan with Docker
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    -v $HOME/Library/Caches:/root/.cache/ \
    aquasec/trivy:latest image python:3.4-alpine

Package Manager Installation

bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy

# CentOS/RHEL/Fedora
sudo vim /etc/yum.repos.d/trivy.repo
# Add:
# [trivy]
# name=Trivy repository
# baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$releasever/$basearch/
# gpgcheck=0
# enabled=1
sudo yum -y update
sudo yum -y install trivy

# Arch Linux
yay -S trivy-bin

# Alpine Linux
apk add trivy

Kubernetes Installation

bash
# Install as a Kubernetes job
kubectl create job trivy --image=aquasec/trivy:latest -- trivy image --exit-code 1 nginx:latest

# Install Trivy Operator for continuous scanning
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/trivy-operator/main/deploy/static/trivy-operator.yaml

# Helm installation
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aqua/trivy-operator \
    --namespace trivy-system \
    --create-namespace \
    --version 0.16.4

Basic Usage

Container Image Scanning

bash
# Basic image scan
trivy image nginx:latest

# Scan specific image with tag
trivy image ubuntu:20.04

# Scan image with specific severity levels
trivy image --severity HIGH,CRITICAL nginx:latest

# Scan image and ignore unfixed vulnerabilities
trivy image --ignore-unfixed nginx:latest

# Scan image with custom format
trivy image --format json nginx:latest

# Scan image and save results to file
trivy image --output result.json --format json nginx:latest

# Scan image with specific vulnerability types
trivy image --vuln-type os,library nginx:latest

# Scan private registry image
trivy image --username myuser --password mypass myregistry.com/myimage:tag

# Scan image with custom timeout
trivy image --timeout 10m nginx:latest

# Scan image and exit with code on vulnerabilities
trivy image --exit-code 1 nginx:latest

Filesystem Scanning

bash
# Scan current directory
trivy fs .

# Scan specific directory
trivy fs /path/to/project

# Scan filesystem with specific file patterns
trivy fs --skip-files "*.test.js,*.spec.js" .

# Scan filesystem for secrets only
trivy fs --scanners secret .

# Scan filesystem for misconfigurations
trivy fs --scanners config .

# Scan filesystem with custom config
trivy fs --config-file trivy.yaml .

# Scan filesystem and ignore specific paths
trivy fs --skip-dirs vendor,node_modules .

# Scan filesystem with license detection
trivy fs --scanners license .

Repository Scanning

bash
# Scan remote Git repository
trivy repo https://github.com/aquasecurity/trivy

# Scan specific branch
trivy repo --branch main https://github.com/aquasecurity/trivy

# Scan repository with authentication
trivy repo --username user --password token https://github.com/private/repo

# Scan repository for specific scanners
trivy repo --scanners vuln,secret https://github.com/aquasecurity/trivy

# Scan repository with custom output
trivy repo --format sarif --output results.sarif https://github.com/aquasecurity/trivy

Kubernetes Scanning

bash
# Scan Kubernetes cluster
trivy k8s cluster

# Scan specific namespace
trivy k8s --namespace kube-system cluster

# Scan specific Kubernetes resource
trivy k8s deployment/nginx

# Scan all resources in namespace
trivy k8s --namespace default all

# Scan with specific report format
trivy k8s --format json --output k8s-report.json cluster

# Scan Kubernetes manifests
trivy k8s --scanners config deployment.yaml

# Scan Helm charts
trivy k8s --scanners config helm-chart/

# Scan with compliance report
trivy k8s --compliance k8s-cis cluster

Advanced Usage

Configuration Management

bash
# Create Trivy configuration file
cat > trivy.yaml << 'EOF'
# Trivy configuration file

# Cache settings
cache:
  dir: /tmp/trivy-cache
  
# Database settings
db:
  skip-update: false
  
# Vulnerability settings
vulnerability:
  type: os,library
  
# Secret settings
secret:
  config: secret-config.yaml
  
# Misconfiguration settings
misconfiguration:
  trace: false
  config-file: misconfig.yaml
  
# Output settings
format: json
output: trivy-results.json

# Severity settings
severity: HIGH,CRITICAL

# Skip settings
skip-files:
  - "*.test.js"
  - "*.spec.js"
  - "test/**"
  
skip-dirs:
  - vendor
  - node_modules
  - .git

# Ignore settings
ignorefile: .trivyignore

# Exit code settings
exit-code: 1

# Timeout settings
timeout: 5m

# Registry settings
registry:
  username: ""
  password: ""
  
# Compliance settings
compliance: docker-cis
EOF

# Use configuration file
trivy image --config trivy.yaml nginx:latest

Custom Policies and Rules

bash
# Create custom policy for misconfigurations
mkdir -p policies/kubernetes
cat > policies/kubernetes/custom-policy.rego << 'EOF'
# METADATA
# title: "Custom Kubernetes Security Policy"
# description: "Custom security checks for Kubernetes resources"
# scope: package
# schemas:
# - input: schema["kubernetes"]
# custom:
#   id: CUSTOM001
#   avd_id: AVD-CUSTOM-001
#   severity: HIGH
#   short_code: no-root-user
#   recommended_action: "Ensure containers do not run as root user"

package builtin.kubernetes.KSV001

import rego.v1

deny contains res if {
    input.kind == "Pod"
    input.spec.securityContext.runAsUser == 0
    res := result.new("Container should not run as root user", {})
}

deny contains res if {
    input.kind == "Deployment"
    input.spec.template.spec.securityContext.runAsUser == 0
    res := result.new("Container should not run as root user", {})
}
EOF

# Scan with custom policy
trivy k8s --policy policies/ deployment.yaml

Secret Detection Configuration

bash
# Create secret detection configuration
cat > secret-config.yaml << 'EOF'
# Secret detection configuration

# Custom secret patterns
rules:
  - id: custom-api-key
    category: general
    title: Custom API Key
    severity: HIGH
    regex: 'custom_api_[a-zA-Z0-9]{32}'
    keywords:
      - custom_api
    
  - id: internal-token
    category: general
    title: Internal Token
    severity: MEDIUM
    regex: 'internal_[a-zA-Z0-9]{16}'
    keywords:
      - internal_

# Allowlist patterns
allowlist:
  description: Allowlist for false positives
  rules:
    - description: Test API keys
      regex: 'test_api_[a-zA-Z0-9]+'
    - description: Example tokens
      regex: 'example_[a-zA-Z0-9]+'

# Global allowlist
global-allowlist:
  - rule-id: generic-api-key
    paths:
      - "test/**"
      - "**/*test*"
EOF

# Scan with custom secret configuration
trivy fs --scanners secret --secret-config secret-config.yaml .

SBOM Generation

bash
# Generate SBOM in CycloneDX format
trivy image --format cyclonedx --output sbom.json nginx:latest

# Generate SBOM in SPDX format
trivy image --format spdx --output sbom.spdx nginx:latest

# Generate SBOM in SPDX-JSON format
trivy image --format spdx-json --output sbom.spdx.json nginx:latest

# Generate SBOM for filesystem
trivy fs --format cyclonedx --output fs-sbom.json .

# Generate SBOM with license information
trivy image --scanners vuln,license --format cyclonedx nginx:latest

# Generate comprehensive SBOM
trivy image --format cyclonedx --output comprehensive-sbom.json \
    --scanners vuln,license,secret nginx:latest

Automation and Scripting

Comprehensive Scanning Script

bash
#!/bin/bash
# trivy_scan.sh - Comprehensive Trivy scanning automation

set -euo pipefail

# Configuration
SCAN_TARGET="${1:-}"
SCAN_TYPE="${2:-image}"
OUTPUT_DIR="/tmp/trivy-results"
REPORT_FORMAT="json"
SEVERITY_LEVELS="HIGH,CRITICAL"
SLACK_WEBHOOK="${SLACK_WEBHOOK:-}"
EMAIL_RECIPIENTS="${EMAIL_RECIPIENTS:-}"

# Logging function
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$OUTPUT_DIR/scan.log"
}

# Create output directory
mkdir -p "$OUTPUT_DIR"

# Validate input
if [ -z "$SCAN_TARGET" ]; then
    echo "Usage: $0 <target> [scan_type]"
    echo "Scan types: image, fs, repo, k8s"
    exit 1
fi

# Update Trivy database
update_database() {
    log "Updating Trivy vulnerability database..."
    trivy image --download-db-only
    log "Database update completed"
}

# Scan container image
scan_image() {
    local image="$1"
    local output_file="$OUTPUT_DIR/image-$(echo "$image" | tr '/:' '_')-$(date +%Y%m%d_%H%M%S)"
    
    log "Scanning container image: $image"
    
    # Comprehensive scan
    trivy image \
        --format "$REPORT_FORMAT" \
        --output "${output_file}.json" \
        --severity "$SEVERITY_LEVELS" \
        --vuln-type os,library \
        --scanners vuln,secret,config \
        --exit-code 0 \
        "$image"
    
    # Generate SBOM
    trivy image \
        --format cyclonedx \
        --output "${output_file}-sbom.json" \
        --scanners vuln,license \
        "$image"
    
    # Generate human-readable report
    trivy image \
        --format table \
        --output "${output_file}.txt" \
        --severity "$SEVERITY_LEVELS" \
        "$image"
    
    log "Image scan completed: $output_file"
    echo "$output_file"
}

# Scan filesystem
scan_filesystem() {
    local path="$1"
    local output_file="$OUTPUT_DIR/fs-$(basename "$path")-$(date +%Y%m%d_%H%M%S)"
    
    log "Scanning filesystem: $path"
    
    # Comprehensive filesystem scan
    trivy fs \
        --format "$REPORT_FORMAT" \
        --output "${output_file}.json" \
        --severity "$SEVERITY_LEVELS" \
        --scanners vuln,secret,config,license \
        --skip-dirs vendor,node_modules,.git \
        --exit-code 0 \
        "$path"
    
    # Generate SBOM
    trivy fs \
        --format cyclonedx \
        --output "${output_file}-sbom.json" \
        --scanners vuln,license \
        "$path"
    
    log "Filesystem scan completed: $output_file"
    echo "$output_file"
}

# Scan repository
scan_repository() {
    local repo="$1"
    local output_file="$OUTPUT_DIR/repo-$(echo "$repo" | tr '/:' '_')-$(date +%Y%m%d_%H%M%S)"
    
    log "Scanning repository: $repo"
    
    # Repository scan
    trivy repo \
        --format "$REPORT_FORMAT" \
        --output "${output_file}.json" \
        --severity "$SEVERITY_LEVELS" \
        --scanners vuln,secret,config \
        --exit-code 0 \
        "$repo"
    
    log "Repository scan completed: $output_file"
    echo "$output_file"
}

# Scan Kubernetes cluster
scan_kubernetes() {
    local target="$1"
    local output_file="$OUTPUT_DIR/k8s-$(echo "$target" | tr '/:' '_')-$(date +%Y%m%d_%H%M%S)"
    
    log "Scanning Kubernetes: $target"
    
    # Kubernetes scan
    trivy k8s \
        --format "$REPORT_FORMAT" \
        --output "${output_file}.json" \
        --severity "$SEVERITY_LEVELS" \
        --scanners vuln,config \
        --exit-code 0 \
        "$target"
    
    # Compliance scan
    trivy k8s \
        --compliance k8s-cis \
        --format json \
        --output "${output_file}-compliance.json" \
        "$target"
    
    log "Kubernetes scan completed: $output_file"
    echo "$output_file"
}

# Analyze scan results
analyze_results() {
    local result_file="$1"
    
    if [ ! -f "$result_file.json" ]; then
        log "Result file not found: $result_file.json"
        return 1
    fi
    
    log "Analyzing scan results..."
    
    # Count vulnerabilities by severity
    local critical_count high_count medium_count low_count
    critical_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' "$result_file.json" 2>/dev/null || echo "0")
    high_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' "$result_file.json" 2>/dev/null || echo "0")
    medium_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "MEDIUM")] | length' "$result_file.json" 2>/dev/null || echo "0")
    low_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "LOW")] | length' "$result_file.json" 2>/dev/null || echo "0")
    
    # Count secrets
    local secret_count
    secret_count=$(jq '[.Results[]?.Secrets[]?] | length' "$result_file.json" 2>/dev/null || echo "0")
    
    # Count misconfigurations
    local misconfig_count
    misconfig_count=$(jq '[.Results[]?.Misconfigurations[]?] | length' "$result_file.json" 2>/dev/null || echo "0")
    
    # Generate summary
    cat > "$result_file-summary.txt" << EOF
Trivy Scan Summary
==================
Scan Target: $SCAN_TARGET
Scan Type: $SCAN_TYPE
Scan Date: $(date)

Vulnerability Summary:
- Critical: $critical_count
- High: $high_count
- Medium: $medium_count
- Low: $low_count

Security Issues:
- Secrets Found: $secret_count
- Misconfigurations: $misconfig_count

Total Issues: $((critical_count + high_count + medium_count + low_count + secret_count + misconfig_count))
EOF
    
    log "Analysis completed. Summary saved to: $result_file-summary.txt"
    
    # Return exit code based on critical/high vulnerabilities
    if [ "$((critical_count + high_count))" -gt 0 ]; then
        return 1
    else
        return 0
    fi
}

# Send notifications
send_notifications() {
    local result_file="$1"
    local summary_file="$result_file-summary.txt"
    
    if [ ! -f "$summary_file" ]; then
        log "Summary file not found: $summary_file"
        return 1
    fi
    
    local summary_content
    summary_content=$(cat "$summary_file")
    
    # Send Slack notification
    if [ -n "$SLACK_WEBHOOK" ]; then
        log "Sending Slack notification..."
        curl -X POST -H 'Content-type: application/json' \
            --data "{\"text\":\"🔍 Trivy Scan Results\n\`\`\`\n$summary_content\n\`\`\`\"}" \
            "$SLACK_WEBHOOK" 2>/dev/null || log "Failed to send Slack notification"
    fi
    
    # Send email notification
    if [ -n "$EMAIL_RECIPIENTS" ] && command -v mail >/dev/null 2>&1; then
        log "Sending email notification..."
        echo "$summary_content" | mail -s "Trivy Scan Results - $SCAN_TARGET" "$EMAIL_RECIPIENTS" || log "Failed to send email notification"
    fi
}

# Generate HTML report
generate_html_report() {
    local result_file="$1"
    local html_file="$result_file.html"
    
    log "Generating HTML report..."
    
    # Convert JSON to HTML (basic implementation)
    cat > "$html_file" << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>Trivy Scan Report</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .header { background-color: #f0f0f0; padding: 10px; border-radius: 5px; }
        .critical { color: #d32f2f; font-weight: bold; }
        .high { color: #f57c00; font-weight: bold; }
        .medium { color: #fbc02d; font-weight: bold; }
        .low { color: #388e3c; }
        table { border-collapse: collapse; width: 100%; margin-top: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
    </style>
</head>
<body>
    <div class="header">
        <h1>Trivy Security Scan Report</h1>
        <p><strong>Target:</strong> SCAN_TARGET_PLACEHOLDER</p>
        <p><strong>Date:</strong> DATE_PLACEHOLDER</p>
    </div>
    
    <div id="summary">
        <h2>Summary</h2>
        <div id="summary-content">
            <!-- Summary will be inserted here -->
        </div>
    </div>
    
    <div id="details">
        <h2>Detailed Results</h2>
        <div id="results-content">
            <!-- Detailed results will be inserted here -->
        </div>
    </div>
    
    <script>
        // Load and display JSON results
        // This would be populated with actual data processing
    </script>
</body>
</html>
EOF
    
    # Replace placeholders
    sed -i "s/SCAN_TARGET_PLACEHOLDER/$SCAN_TARGET/g" "$html_file"
    sed -i "s/DATE_PLACEHOLDER/$(date)/g" "$html_file"
    
    log "HTML report generated: $html_file"
}

# Main execution
main() {
    log "Starting Trivy scan automation..."
    log "Target: $SCAN_TARGET, Type: $SCAN_TYPE"
    
    # Update database
    update_database
    
    # Perform scan based on type
    local result_file
    case "$SCAN_TYPE" in
        "image")
            result_file=$(scan_image "$SCAN_TARGET")
            ;;
        "fs")
            result_file=$(scan_filesystem "$SCAN_TARGET")
            ;;
        "repo")
            result_file=$(scan_repository "$SCAN_TARGET")
            ;;
        "k8s")
            result_file=$(scan_kubernetes "$SCAN_TARGET")
            ;;
        *)
            log "Unknown scan type: $SCAN_TYPE"
            exit 1
            ;;
    esac
    
    # Analyze results
    if analyze_results "$result_file"; then
        log "✅ Scan completed successfully - No critical/high vulnerabilities found"
        exit_code=0
    else
        log "⚠️  Scan completed with critical/high vulnerabilities found"
        exit_code=1
    fi
    
    # Generate additional reports
    generate_html_report "$result_file"
    
    # Send notifications
    send_notifications "$result_file"
    
    log "Trivy scan automation completed"
    log "Results available in: $OUTPUT_DIR"
    
    exit $exit_code
}

# Execute main function
main "$@"

CI/CD Integration Scripts

bash
# Jenkins Pipeline
cat > Jenkinsfile << 'EOF'
pipeline {
    agent any
    
    environment {
        TRIVY_CACHE_DIR = '/tmp/trivy-cache'
        TRIVY_NO_PROGRESS = 'true'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build Image') {
            steps {
                script {
                    def image = docker.build("${env.JOB_NAME}:${env.BUILD_NUMBER}")
                    env.IMAGE_NAME = image.id
                }
            }
        }
        
        stage('Trivy Scan') {
            parallel {
                stage('Vulnerability Scan') {
                    steps {
                        script {
                            sh '''
                                trivy image \
                                    --format json \
                                    --output trivy-vuln-report.json \
                                    --severity HIGH,CRITICAL \
                                    --exit-code 1 \
                                    ${IMAGE_NAME}
                            '''
                        }
                    }
                    post {
                        always {
                            archiveArtifacts artifacts: 'trivy-vuln-report.json', fingerprint: true
                        }
                    }
                }
                
                stage('Secret Scan') {
                    steps {
                        script {
                            sh '''
                                trivy fs \
                                    --format json \
                                    --output trivy-secret-report.json \
                                    --scanners secret \
                                    --exit-code 0 \
                                    .
                            '''
                        }
                    }
                    post {
                        always {
                            archiveArtifacts artifacts: 'trivy-secret-report.json', fingerprint: true
                        }
                    }
                }
                
                stage('Config Scan') {
                    steps {
                        script {
                            sh '''
                                trivy fs \
                                    --format json \
                                    --output trivy-config-report.json \
                                    --scanners config \
                                    --exit-code 0 \
                                    .
                            '''
                        }
                    }
                    post {
                        always {
                            archiveArtifacts artifacts: 'trivy-config-report.json', fingerprint: true
                        }
                    }
                }
            }
        }
        
        stage('Generate SBOM') {
            steps {
                script {
                    sh '''
                        trivy image \
                            --format cyclonedx \
                            --output sbom.json \
                            ${IMAGE_NAME}
                    '''
                }
            }
            post {
                always {
                    archiveArtifacts artifacts: 'sbom.json', fingerprint: true
                }
            }
        }
        
        stage('Security Gate') {
            steps {
                script {
                    def vulnReport = readJSON file: 'trivy-vuln-report.json'
                    def criticalCount = 0
                    def highCount = 0
                    
                    vulnReport.Results.each { result ->
                        if (result.Vulnerabilities) {
                            result.Vulnerabilities.each { vuln ->
                                if (vuln.Severity == 'CRITICAL') criticalCount++
                                if (vuln.Severity == 'HIGH') highCount++
                            }
                        }
                    }
                    
                    echo "Critical vulnerabilities: ${criticalCount}"
                    echo "High vulnerabilities: ${highCount}"
                    
                    if (criticalCount > 0) {
                        error("Build failed due to ${criticalCount} critical vulnerabilities")
                    }
                    
                    if (highCount > 5) {
                        unstable("Build marked unstable due to ${highCount} high vulnerabilities")
                    }
                }
            }
        }
    }
    
    post {
        always {
            publishHTML([
                allowMissing: false,
                alwaysLinkToLastBuild: true,
                keepAll: true,
                reportDir: '.',
                reportFiles: 'trivy-*.json',
                reportName: 'Trivy Security Report'
            ])
        }
        failure {
            emailext (
                subject: "Security Scan Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
                body: "Security vulnerabilities found. Check console output for details.",
                to: "${env.CHANGE_AUTHOR_EMAIL}"
            )
        }
    }
}
EOF

# GitHub Actions Workflow
cat > .github/workflows/trivy.yml << 'EOF'
name: Trivy Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Build Docker image
      run: |
        docker build -t test-image:${{ github.sha }} .
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: 'test-image:${{ github.sha }}'
        format: 'sarif'
        output: 'trivy-results.sarif'
        severity: 'CRITICAL,HIGH'
    
    - name: Upload Trivy scan results to GitHub Security tab
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: 'trivy-results.sarif'
    
    - name: Run Trivy filesystem scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'json'
        output: 'trivy-fs-results.json'
        severity: 'CRITICAL,HIGH'
    
    - name: Generate SBOM
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: 'test-image:${{ github.sha }}'
        format: 'cyclonedx'
        output: 'sbom.json'
    
    - name: Upload scan results
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: trivy-results
        path: |
          trivy-results.sarif
          trivy-fs-results.json
          sbom.json
    
    - name: Security Gate
      run: |
        # Check for critical vulnerabilities
        CRITICAL_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-fs-results.json)
        HIGH_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' trivy-fs-results.json)
        
        echo "Critical vulnerabilities: $CRITICAL_COUNT"
        echo "High vulnerabilities: $HIGH_COUNT"
        
        if [ "$CRITICAL_COUNT" -gt 0 ]; then
          echo "::error::Build failed due to $CRITICAL_COUNT critical vulnerabilities"
          exit 1
        fi
        
        if [ "$HIGH_COUNT" -gt 10 ]; then
          echo "::warning::Build has $HIGH_COUNT high vulnerabilities"
        fi

  trivy-config-scan:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
    
    - name: Run Trivy config scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'config'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-config-results.sarif'
    
    - name: Upload config scan results
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: 'trivy-config-results.sarif'
EOF

# GitLab CI Configuration
cat > .gitlab-ci.yml << 'EOF'
stages:
  - build
  - security-scan
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  TRIVY_NO_PROGRESS: "true"
  TRIVY_CACHE_DIR: ".trivycache/"

cache:
  paths:
    - .trivycache/

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main
    - develop

trivy-vulnerability-scan:
  stage: security-scan
  image: aquasec/trivy:latest
  script:
    - trivy image --format json --output trivy-vuln-report.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - trivy image --format template --template "@contrib/html.tpl" --output trivy-vuln-report.html $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  artifacts:
    reports:
      container_scanning: trivy-vuln-report.json
    paths:
      - trivy-vuln-report.html
    expire_in: 1 week
  only:
    - main
    - develop

trivy-config-scan:
  stage: security-scan
  image: aquasec/trivy:latest
  script:
    - trivy fs --format json --output trivy-config-report.json .
    - trivy fs --format template --template "@contrib/html.tpl" --output trivy-config-report.html .
  artifacts:
    paths:
      - trivy-config-report.json
      - trivy-config-report.html
    expire_in: 1 week
  only:
    - main
    - develop

trivy-sbom-generation:
  stage: security-scan
  image: aquasec/trivy:latest
  script:
    - trivy image --format cyclonedx --output sbom.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  artifacts:
    paths:
      - sbom.json
    expire_in: 1 week
  only:
    - main
    - develop
EOF

Kubernetes Monitoring Script

bash
#!/bin/bash
# k8s_trivy_monitor.sh - Continuous Kubernetes security monitoring with Trivy

set -euo pipefail

# Configuration
NAMESPACE="${NAMESPACE:-default}"
OUTPUT_DIR="/tmp/k8s-trivy-results"
SLACK_WEBHOOK="${SLACK_WEBHOOK:-}"
SCAN_INTERVAL="${SCAN_INTERVAL:-3600}"  # 1 hour

# Create output directory
mkdir -p "$OUTPUT_DIR"

# Logging function
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$OUTPUT_DIR/monitor.log"
}

# Get all container images in namespace
get_container_images() {
    local namespace="$1"
    
    kubectl get pods -n "$namespace" -o jsonpath='{.items[*].spec.containers[*].image}' | \
        tr ' ' '\n' | sort -u | grep -v '^$'
}

# Scan container image
scan_image() {
    local image="$1"
    local output_file="$OUTPUT_DIR/$(echo "$image" | tr '/:' '_')-$(date +%Y%m%d_%H%M%S).json"
    
    log "Scanning image: $image"
    
    trivy image \
        --format json \
        --output "$output_file" \
        --severity HIGH,CRITICAL \
        --vuln-type os,library \
        --exit-code 0 \
        "$image" 2>/dev/null || {
        log "Failed to scan image: $image"
        return 1
    }
    
    echo "$output_file"
}

# Scan Kubernetes cluster
scan_cluster() {
    local namespace="$1"
    local output_file="$OUTPUT_DIR/cluster-$namespace-$(date +%Y%m%d_%H%M%S).json"
    
    log "Scanning Kubernetes cluster namespace: $namespace"
    
    trivy k8s \
        --namespace "$namespace" \
        --format json \
        --output "$output_file" \
        --severity HIGH,CRITICAL \
        cluster 2>/dev/null || {
        log "Failed to scan cluster namespace: $namespace"
        return 1
    }
    
    echo "$output_file"
}

# Analyze scan results
analyze_results() {
    local result_file="$1"
    
    if [ ! -f "$result_file" ]; then
        log "Result file not found: $result_file"
        return 1
    fi
    
    local critical_count high_count
    critical_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' "$result_file" 2>/dev/null || echo "0")
    high_count=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' "$result_file" 2>/dev/null || echo "0")
    
    echo "$critical_count,$high_count"
}

# Send alert
send_alert() {
    local message="$1"
    
    if [ -n "$SLACK_WEBHOOK" ]; then
        curl -X POST -H 'Content-type: application/json' \
            --data "{\"text\":\"🚨 Kubernetes Security Alert: $message\"}" \
            "$SLACK_WEBHOOK" 2>/dev/null || log "Failed to send alert"
    fi
    
    log "ALERT: $message"
}

# Generate summary report
generate_summary() {
    local namespace="$1"
    
    log "Generating summary report for namespace: $namespace"
    
    local summary_file="$OUTPUT_DIR/summary-$namespace-$(date +%Y%m%d_%H%M%S).txt"
    local total_critical=0
    local total_high=0
    local scanned_images=0
    
    cat > "$summary_file" << EOF
Kubernetes Security Summary Report
==================================
Namespace: $namespace
Generated: $(date)

Image Scan Results:
EOF
    
    # Scan all images in namespace
    while IFS= read -r image; do
        if [ -n "$image" ]; then
            local result_file
            result_file=$(scan_image "$image")
            
            if [ -n "$result_file" ] && [ -f "$result_file" ]; then
                local counts
                counts=$(analyze_results "$result_file")
                local critical_count="${counts%,*}"
                local high_count="${counts#*,}"
                
                total_critical=$((total_critical + critical_count))
                total_high=$((total_high + high_count))
                scanned_images=$((scanned_images + 1))
                
                echo "  $image: Critical=$critical_count, High=$high_count" >> "$summary_file"
                
                # Alert on critical vulnerabilities
                if [ "$critical_count" -gt 0 ]; then
                    send_alert "Critical vulnerabilities found in $image: $critical_count critical, $high_count high"
                fi
            fi
        fi
    done < <(get_container_images "$namespace")
    
    # Add cluster scan results
    local cluster_result
    cluster_result=$(scan_cluster "$namespace")
    
    if [ -n "$cluster_result" ] && [ -f "$cluster_result" ]; then
        local cluster_counts
        cluster_counts=$(analyze_results "$cluster_result")
        local cluster_critical="${cluster_counts%,*}"
        local cluster_high="${cluster_counts#*,}"
        
        echo "" >> "$summary_file"
        echo "Cluster Configuration Scan:" >> "$summary_file"
        echo "  Critical: $cluster_critical" >> "$summary_file"
        echo "  High: $cluster_high" >> "$summary_file"
    fi
    
    # Add totals
    cat >> "$summary_file" << EOF

Summary:
========
Total Images Scanned: $scanned_images
Total Critical Vulnerabilities: $total_critical
Total High Vulnerabilities: $total_high

Recommendations:
- Update images with critical vulnerabilities immediately
- Review and remediate high-severity vulnerabilities
- Implement automated vulnerability scanning in CI/CD pipeline
- Consider using admission controllers to prevent vulnerable images
EOF
    
    log "Summary report generated: $summary_file"
    
    # Send summary alert if significant issues found
    if [ "$total_critical" -gt 0 ] || [ "$total_high" -gt 10 ]; then
        send_alert "Namespace $namespace scan completed: $total_critical critical, $total_high high vulnerabilities found across $scanned_images images"
    fi
}

# Continuous monitoring loop
monitor_loop() {
    log "Starting continuous Kubernetes monitoring..."
    
    while true; do
        log "Starting scan cycle..."
        
        # Get all namespaces or scan specific namespace
        if [ "$NAMESPACE" = "all" ]; then
            while IFS= read -r ns; do
                if [ -n "$ns" ] && [ "$ns" != "kube-system" ] && [ "$ns" != "kube-public" ]; then
                    generate_summary "$ns"
                fi
            done < <(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}' | tr ' ' '\n')
        else
            generate_summary "$NAMESPACE"
        fi
        
        log "Scan cycle completed. Sleeping for $SCAN_INTERVAL seconds..."
        sleep "$SCAN_INTERVAL"
    done
}

# Cleanup old results
cleanup_old_results() {
    local retention_days="${1:-7}"
    
    log "Cleaning up results older than $retention_days days..."
    find "$OUTPUT_DIR" -name "*.json" -mtime +$retention_days -delete
    find "$OUTPUT_DIR" -name "*.txt" -mtime +$retention_days -delete
}

# Main execution
main() {
    case "${1:-monitor}" in
        "monitor")
            monitor_loop
            ;;
        "scan")
            generate_summary "$NAMESPACE"
            ;;
        "cleanup")
            cleanup_old_results "${2:-7}"
            ;;
        "images")
            get_container_images "$NAMESPACE"
            ;;
        *)
            echo "Usage: $0 {monitor|scan|cleanup [days]|images}"
            echo "Environment variables:"
            echo "  NAMESPACE: Kubernetes namespace to scan (default: default, use 'all' for all namespaces)"
            echo "  SLACK_WEBHOOK: Slack webhook URL for alerts"
            echo "  SCAN_INTERVAL: Scan interval in seconds (default: 3600)"
            exit 1
            ;;
    esac
}

# Execute main function
main "$@"

Integration Examples

SIEM Integration

bash
# Splunk integration
cat > /etc/filebeat/conf.d/trivy.yml << 'EOF'
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/trivy/*.json
  json.keys_under_root: true
  json.add_error_key: true
  fields:
    logtype: trivy
    source: security_scan
  fields_under_root: true

output.elasticsearch:
  hosts: ["localhost:9200"]
  index: "trivy-%{+yyyy.MM.dd}"
  template.name: "trivy"
  template.pattern: "trivy-*"

processors:
- add_host_metadata:
    when.not.contains.tags: forwarded
- add_docker_metadata: ~
- add_kubernetes_metadata: ~
EOF

# ELK Stack dashboard configuration
cat > trivy-dashboard.json << 'EOF'
{
  "version": "7.15.0",
  "objects": [
    {
      "id": "trivy-vulnerability-overview",
      "type": "dashboard",
      "attributes": {
        "title": "Trivy Vulnerability Overview",
        "hits": 0,
        "description": "Overview of vulnerabilities detected by Trivy",
        "panelsJSON": "[{\"version\":\"7.15.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":15,\"i\":\"1\"},\"panelIndex\":\"1\",\"embeddableConfig\":{},\"panelRefName\":\"panel_1\"}]",
        "optionsJSON": "{\"useMargins\":true,\"syncColors\":false,\"hidePanelTitles\":false}",
        "version": 1,
        "timeRestore": false,
        "kibanaSavedObjectMeta": {
          "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"
        }
      }
    }
  ]
}
EOF

Vulnerability Management Integration

bash
# DefectDojo integration
cat > defectdojo_import.py << 'EOF'
#!/usr/bin/env python3
"""
DefectDojo Trivy Integration
Import Trivy scan results into DefectDojo
"""

import json
import requests
import argparse
import sys
from datetime import datetime

class DefectDojoClient:
    def __init__(self, url, api_key):
        self.url = url.rstrip('/')
        self.api_key = api_key
        self.session = requests.Session()
        self.session.headers.update({
            'Authorization': f'Token {api_key}',
            'Content-Type': 'application/json'
        })
    
    def create_engagement(self, product_id, name, description):
        """Create a new engagement"""
        data = {
            'name': name,
            'description': description,
            'product': product_id,
            'target_start': datetime.now().isoformat(),
            'target_end': datetime.now().isoformat(),
            'status': 'In Progress'
        }
        
        response = self.session.post(f'{self.url}/api/v2/engagements/', json=data)
        response.raise_for_status()
        return response.json()['id']
    
    def import_scan(self, engagement_id, scan_file, scan_type='Trivy Scan'):
        """Import scan results"""
        with open(scan_file, 'rb') as f:
            files = {'file': f}
            data = {
                'engagement': engagement_id,
                'scan_type': scan_type,
                'verified': True,
                'active': True,
                'minimum_severity': 'Info'
            }
            
            response = self.session.post(
                f'{self.url}/api/v2/import-scan/',
                files=files,
                data=data
            )
            response.raise_for_status()
            return response.json()

def main():
    parser = argparse.ArgumentParser(description='Import Trivy results to DefectDojo')
    parser.add_argument('--url', required=True, help='DefectDojo URL')
    parser.add_argument('--api-key', required=True, help='DefectDojo API key')
    parser.add_argument('--product-id', required=True, type=int, help='Product ID')
    parser.add_argument('--scan-file', required=True, help='Trivy scan result file')
    parser.add_argument('--engagement-name', required=True, help='Engagement name')
    
    args = parser.parse_args()
    
    try:
        client = DefectDojoClient(args.url, args.api_key)
        
        # Create engagement
        engagement_id = client.create_engagement(
            args.product_id,
            args.engagement_name,
            f'Trivy security scan - {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
        )
        
        print(f"Created engagement: {engagement_id}")
        
        # Import scan
        result = client.import_scan(engagement_id, args.scan_file)
        print(f"Imported scan: {result}")
        
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)

if __name__ == '__main__':
    main()
EOF

chmod +x defectdojo_import.py

# JIRA integration for vulnerability tracking
cat > jira_integration.py << 'EOF'
#!/usr/bin/env python3
"""
JIRA Trivy Integration
Create JIRA tickets for critical vulnerabilities
"""

import json
import requests
import argparse
from jira import JIRA

def create_vulnerability_ticket(jira_client, project_key, vulnerability):
    """Create JIRA ticket for vulnerability"""
    
    summary = f"[SECURITY] {vulnerability['Title']} - {vulnerability['Severity']}"
    description = f"""
*Vulnerability Details:*
- *Package:* {vulnerability.get('PkgName', 'N/A')}
- *Installed Version:* {vulnerability.get('InstalledVersion', 'N/A')}
- *Fixed Version:* {vulnerability.get('FixedVersion', 'N/A')}
- *Severity:* {vulnerability['Severity']}
- *CVE:* {vulnerability.get('VulnerabilityID', 'N/A')}

*Description:*
{vulnerability.get('Description', 'No description available')}

*References:*
{chr(10).join(vulnerability.get('References', []))}

*Remediation:*
Update package to fixed version: {vulnerability.get('FixedVersion', 'No fix available')}
"""
    
    issue_dict = {
        'project': {'key': project_key},
        'summary': summary,
        'description': description,
        'issuetype': {'name': 'Bug'},
        'priority': {'name': 'High' if vulnerability['Severity'] == 'CRITICAL' else 'Medium'},
        'labels': ['security', 'vulnerability', vulnerability['Severity'].lower()]
    }
    
    return jira_client.create_issue(fields=issue_dict)

def main():
    parser = argparse.ArgumentParser(description='Create JIRA tickets for Trivy vulnerabilities')
    parser.add_argument('--jira-url', required=True, help='JIRA URL')
    parser.add_argument('--username', required=True, help='JIRA username')
    parser.add_argument('--api-token', required=True, help='JIRA API token')
    parser.add_argument('--project-key', required=True, help='JIRA project key')
    parser.add_argument('--scan-file', required=True, help='Trivy scan result file')
    parser.add_argument('--severity', default='CRITICAL,HIGH', help='Severity levels to create tickets for')
    
    args = parser.parse_args()
    
    # Connect to JIRA
    jira = JIRA(server=args.jira_url, basic_auth=(args.username, args.api_token))
    
    # Load scan results
    with open(args.scan_file, 'r') as f:
        scan_data = json.load(f)
    
    severity_filter = args.severity.split(',')
    created_tickets = []
    
    # Process vulnerabilities
    for result in scan_data.get('Results', []):
        for vulnerability in result.get('Vulnerabilities', []):
            if vulnerability['Severity'] in severity_filter:
                try:
                    ticket = create_vulnerability_ticket(jira, args.project_key, vulnerability)
                    created_tickets.append(ticket.key)
                    print(f"Created ticket: {ticket.key} for {vulnerability['VulnerabilityID']}")
                except Exception as e:
                    print(f"Failed to create ticket for {vulnerability.get('VulnerabilityID', 'unknown')}: {e}")
    
    print(f"Created {len(created_tickets)} tickets: {', '.join(created_tickets)}")

if __name__ == '__main__':
    main()
EOF

chmod +x jira_integration.py

Troubleshooting

Common Issues and Solutions

bash
# Database update issues
trivy image --reset  # Reset database
trivy image --download-db-only  # Download database only

# Permission issues
sudo chown -R $(whoami) ~/.cache/trivy/

# Network connectivity issues
trivy image --insecure nginx:latest  # Skip TLS verification
trivy image --timeout 10m nginx:latest  # Increase timeout

# Memory issues
trivy image --no-progress nginx:latest  # Disable progress bar
export TRIVY_CACHE_DIR=/tmp/trivy-cache  # Use different cache directory

# Registry authentication
trivy image --username user --password pass registry.com/image:tag
docker login registry.com && trivy image registry.com/image:tag

# Debug mode
trivy image --debug nginx:latest

# Clear cache
trivy image --clear-cache

# Check version and update
trivy version
trivy image --download-db-only --db-repository ghcr.io/aquasecurity/trivy-db

Performance Optimization

bash
# Optimize cache settings
export TRIVY_CACHE_DIR=/fast/storage/trivy-cache
export TRIVY_TEMP_DIR=/fast/storage/trivy-temp

# Parallel scanning
cat > parallel_scan.sh << 'EOF'
#!/bin/bash
# Parallel image scanning

images=(
    "nginx:latest"
    "ubuntu:20.04"
    "python:3.9"
    "node:16"
)

# Function to scan image
scan_image() {
    local image="$1"
    echo "Scanning $image..."
    trivy image --format json --output "${image//[:\/]/_}.json" "$image"
    echo "Completed $image"
}

# Export function for parallel execution
export -f scan_image

# Run scans in parallel
printf '%s\n' "${images[@]}" | xargs -n 1 -P 4 -I {} bash -c 'scan_image "$@"' _ {}

echo "All scans completed"
EOF

chmod +x parallel_scan.sh

Security Considerations

Best Practices

  1. Database Security:

    • Regularly update vulnerability database
    • Use trusted database sources
    • Verify database integrity
  2. Credential Management:

    • Use secure credential storage
    • Rotate registry credentials regularly
    • Implement least privilege access
  3. Result Handling:

    • Secure scan result storage
    • Implement proper access controls
    • Encrypt sensitive scan data
  4. CI/CD Integration:

    • Implement security gates
    • Use fail-fast approaches
    • Monitor scan performance
  5. Compliance:

    • Regular compliance scanning
    • Document remediation efforts
    • Track vulnerability metrics

Conclusion

Trivy provides comprehensive security scanning capabilities for modern DevSecOps environments. This cheatsheet covers installation, configuration, advanced usage, automation, CI/CD integration, and troubleshooting. Regular scanning and proper integration ensure robust security posture across container and cloud-native environments.