Trivy
Send Trivy vulnerability scan results to Parseable
Collect and analyze Trivy vulnerability scan results in Parseable for comprehensive security monitoring.
Overview
Integrate Trivy with Parseable to:
- Vulnerability Tracking - Track vulnerabilities across your infrastructure
- Container Security - Scan container images for known vulnerabilities
- IaC Security - Detect misconfigurations in infrastructure code
- Compliance - Monitor security compliance over time
Prerequisites
- Trivy installed (CLI or Operator)
- Parseable instance accessible
- CI/CD pipeline or scheduled scanning
What is Trivy?
Trivy is a comprehensive security scanner that detects:
- Vulnerabilities - CVEs in OS packages and application dependencies
- Misconfigurations - Security issues in IaC (Terraform, Kubernetes, etc.)
- Secrets - Hardcoded secrets and credentials
- Licenses - License compliance issues
Method 1: CI/CD Integration
Send scan results from your CI/CD pipeline.
GitHub Actions
name: Security Scan
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * *' # Daily scan
jobs:
trivy-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'json'
output: 'trivy-results.json'
- name: Send results to Parseable
run: |
# Transform Trivy output to Parseable format
jq -c '[.Results[] | .Vulnerabilities[]? | {
timestamp: now | strftime("%Y-%m-%dT%H:%M:%SZ"),
scan_type: "filesystem",
repository: "${{ github.repository }}",
ref: "${{ github.ref }}",
sha: "${{ github.sha }}",
vulnerability_id: .VulnerabilityID,
pkg_name: .PkgName,
installed_version: .InstalledVersion,
fixed_version: .FixedVersion,
severity: .Severity,
title: .Title,
description: .Description,
cvss_score: .CVSS.nvd.V3Score
}]' trivy-results.json > parseable-payload.json
curl -X POST "${{ secrets.PARSEABLE_URL }}/api/v1/ingest" \
-H "Authorization: Basic ${{ secrets.PARSEABLE_AUTH }}" \
-H "X-P-Stream: trivy-vulnerabilities" \
-H "Content-Type: application/json" \
-d @parseable-payload.jsonContainer Image Scanning
name: Container Security Scan
on:
push:
branches: [main]
jobs:
scan-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy scanner
run: |
trivy image --format json --output trivy-results.json myapp:${{ github.sha }}
- name: Send to Parseable
run: |
jq -c '[.Results[] | .Vulnerabilities[]? | {
timestamp: now | strftime("%Y-%m-%dT%H:%M:%SZ"),
scan_type: "container",
image: "myapp:${{ github.sha }}",
repository: "${{ github.repository }}",
target: .Target,
vulnerability_id: .VulnerabilityID,
pkg_name: .PkgName,
installed_version: .InstalledVersion,
fixed_version: .FixedVersion,
severity: .Severity,
title: .Title
}]' trivy-results.json > payload.json
curl -X POST "${{ secrets.PARSEABLE_URL }}/api/v1/ingest" \
-H "Authorization: Basic ${{ secrets.PARSEABLE_AUTH }}" \
-H "X-P-Stream: trivy-vulnerabilities" \
-H "Content-Type: application/json" \
-d @payload.jsonGitLab CI
trivy-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --format json --output trivy-results.json $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- |
apk add --no-cache jq curl
jq -c '[.Results[] | .Vulnerabilities[]? | {
timestamp: now | strftime("%Y-%m-%dT%H:%M:%SZ"),
scan_type: "container",
image: "'$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA'",
project: "'$CI_PROJECT_PATH'",
vulnerability_id: .VulnerabilityID,
pkg_name: .PkgName,
severity: .Severity,
title: .Title
}]' trivy-results.json > payload.json
curl -X POST "${PARSEABLE_URL}/api/v1/ingest" \
-H "Authorization: Basic ${PARSEABLE_AUTH}" \
-H "X-P-Stream: trivy-vulnerabilities" \
-H "Content-Type: application/json" \
-d @payload.jsonMethod 2: Trivy Operator
Use the Trivy Operator for continuous Kubernetes scanning.
Install Trivy Operator
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aqua/trivy-operator \
--namespace trivy-system \
--create-namespaceCollect Reports with Fluent Bit
# fluent-bit-config.yaml
service:
flush: 30
log_level: info
pipeline:
inputs:
- name: kubernetes_events
tag: k8s_events
kube_url: https://kubernetes.default.svc
filters:
- name: grep
match: k8s_events
regex: involvedObject.kind VulnerabilityReport
outputs:
- name: http
match: k8s_events
host: parseable
port: 8000
uri: /api/v1/ingest
format: json
header: Authorization Basic YWRtaW46YWRtaW4=
header: X-P-Stream trivy-operatorExport Reports Script
Create a script to export Trivy Operator reports:
#!/bin/bash
# export-trivy-reports.sh
PARSEABLE_URL="${PARSEABLE_URL:-http://parseable:8000}"
PARSEABLE_AUTH="${PARSEABLE_AUTH:-YWRtaW46YWRtaW4=}"
# Get all VulnerabilityReports
kubectl get vulnerabilityreports -A -o json | jq -c '
[.items[] | {
timestamp: .metadata.creationTimestamp,
namespace: .metadata.namespace,
name: .metadata.name,
image: .report.artifact.repository,
tag: .report.artifact.tag,
critical: .report.summary.criticalCount,
high: .report.summary.highCount,
medium: .report.summary.mediumCount,
low: .report.summary.lowCount,
vulnerabilities: [.report.vulnerabilities[] | {
id: .vulnerabilityID,
severity: .severity,
pkg: .resource,
version: .installedVersion,
fixed: .fixedVersion,
title: .title
}]
}]
' > /tmp/vuln-reports.json
curl -X POST "${PARSEABLE_URL}/api/v1/ingest" \
-H "Authorization: Basic ${PARSEABLE_AUTH}" \
-H "X-P-Stream: trivy-operator" \
-H "Content-Type: application/json" \
-d @/tmp/vuln-reports.jsonKubernetes CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: trivy-report-exporter
spec:
schedule: "0 * * * *" # Every hour
jobTemplate:
spec:
template:
spec:
serviceAccountName: trivy-exporter
containers:
- name: exporter
image: bitnami/kubectl:latest
command: ["/bin/bash", "/scripts/export-trivy-reports.sh"]
env:
- name: PARSEABLE_URL
value: "http://parseable:8000"
- name: PARSEABLE_AUTH
valueFrom:
secretKeyRef:
name: parseable-auth
key: auth
volumeMounts:
- name: scripts
mountPath: /scripts
volumes:
- name: scripts
configMap:
name: trivy-exporter-scripts
restartPolicy: OnFailureMethod 3: Direct CLI Integration
Send scan results directly from Trivy CLI.
Wrapper Script
#!/bin/bash
# trivy-to-parseable.sh
PARSEABLE_URL="${PARSEABLE_URL:-http://localhost:8000}"
PARSEABLE_AUTH="${PARSEABLE_AUTH:-YWRtaW46YWRtaW4=}"
STREAM="${STREAM:-trivy-scans}"
# Run Trivy scan
trivy "$@" --format json --output /tmp/trivy-results.json
# Transform and send to Parseable
jq -c '[{
timestamp: now | strftime("%Y-%m-%dT%H:%M:%SZ"),
scan_target: .ArtifactName,
scan_type: .ArtifactType,
results: [.Results[] | {
target: .Target,
class: .Class,
type: .Type,
vulnerabilities: (.Vulnerabilities // []) | length,
critical: [(.Vulnerabilities // [])[] | select(.Severity == "CRITICAL")] | length,
high: [(.Vulnerabilities // [])[] | select(.Severity == "HIGH")] | length,
medium: [(.Vulnerabilities // [])[] | select(.Severity == "MEDIUM")] | length,
low: [(.Vulnerabilities // [])[] | select(.Severity == "LOW")] | length
}]
}]' /tmp/trivy-results.json > /tmp/parseable-payload.json
curl -s -X POST "${PARSEABLE_URL}/api/v1/ingest" \
-H "Authorization: Basic ${PARSEABLE_AUTH}" \
-H "X-P-Stream: ${STREAM}" \
-H "Content-Type: application/json" \
-d @/tmp/parseable-payload.json
echo "Scan results sent to Parseable"Usage:
./trivy-to-parseable.sh image nginx:latest
./trivy-to-parseable.sh fs /path/to/project
./trivy-to-parseable.sh config /path/to/terraformQuerying Trivy Results
Query your vulnerability data in Parseable:
-- Get recent vulnerability scans
SELECT timestamp, scan_target, scan_type,
results[0].critical as critical,
results[0].high as high
FROM "trivy-scans"
ORDER BY timestamp DESC
LIMIT 100
-- Find critical vulnerabilities
SELECT timestamp, image, vulnerability_id, pkg_name, title
FROM "trivy-vulnerabilities"
WHERE severity = 'CRITICAL'
ORDER BY timestamp DESC
-- Vulnerability count by severity
SELECT
severity,
COUNT(*) as count
FROM "trivy-vulnerabilities"
WHERE timestamp > NOW() - INTERVAL '7 days'
GROUP BY severity
ORDER BY
CASE severity
WHEN 'CRITICAL' THEN 1
WHEN 'HIGH' THEN 2
WHEN 'MEDIUM' THEN 3
WHEN 'LOW' THEN 4
END
-- Most vulnerable images
SELECT
image,
COUNT(*) as total_vulns,
SUM(CASE WHEN severity = 'CRITICAL' THEN 1 ELSE 0 END) as critical,
SUM(CASE WHEN severity = 'HIGH' THEN 1 ELSE 0 END) as high
FROM "trivy-vulnerabilities"
WHERE timestamp > NOW() - INTERVAL '24 hours'
GROUP BY image
ORDER BY critical DESC, high DESC
LIMIT 20
-- Vulnerabilities with available fixes
SELECT vulnerability_id, pkg_name, installed_version, fixed_version, severity
FROM "trivy-vulnerabilities"
WHERE fixed_version IS NOT NULL AND fixed_version != ''
ORDER BY
CASE severity
WHEN 'CRITICAL' THEN 1
WHEN 'HIGH' THEN 2
END
LIMIT 50Setting Up Alerts
Create alerts for critical vulnerabilities:
{
"name": "Critical Vulnerability Detected",
"dataset": "trivy-vulnerabilities",
"alertType": "threshold",
"condition": {
"field": "severity",
"operator": "equals",
"value": "CRITICAL"
},
"threshold": 1,
"duration": "5m",
"webhook": {
"url": "https://your-slack-webhook",
"method": "POST"
}
}Best Practices
- Scan Regularly - Run scans on every build and on a schedule
- Track Trends - Monitor vulnerability counts over time
- Prioritize Fixes - Focus on critical and high severity first
- Include Context - Add repository, image, and commit information
- Set SLAs - Define remediation timelines by severity
Troubleshooting
Empty Results
- Verify Trivy is finding vulnerabilities (check raw output)
- Check jq transformation is correct
- Verify Parseable dataset accepts the payload format
Large Payloads
- Split large scans into smaller batches
- Send summary data instead of full vulnerability details
- Use compression if supported
Next Steps
- Set up alerts for vulnerability thresholds
- Create dashboards for security posture
- Configure Falco for runtime security
Was this page helpful?