Vulnerability Gates
Automated vulnerability scanning and threshold enforcement in CI/CD pipelines
Vulnerability Gates
Vulnerability gates automatically scan dependencies, container images, and code for known security vulnerabilities, blocking deployments when critical issues are detected.
Overview
Modern applications depend on hundreds of third-party libraries and base images. Vulnerability gates ensure that:
- No critical CVEs enter production
- Dependency versions are tracked and monitored
- Known vulnerabilities trigger immediate action
- Security teams have visibility into the attack surface
Real-World Impact
Equifax Breach (2017)
- Exploited Apache Struts CVE-2017-5638 (CVSS 10.0)
- Patch available 2 months before breach
- 147 million records compromised
- Cost: $1.4 billion in settlements
- Prevention: Automated vulnerability gates would have blocked the vulnerable version
Popular Scanning Tools
Trivy (Recommended)
Pros:
- Fast, comprehensive scanning (OS packages, language dependencies, IaC, secrets)
- Easy to integrate, single binary
- Excellent SBOM support
- Free and open-source
Cons:
- Limited false positive management
- No central dashboard (use with Trivy Operator for Kubernetes)
Grype
Pros:
- Fast scans with low memory footprint
- Works with Syft SBOMs
- Good vulnerability database coverage
Cons:
- Fewer scan types than Trivy (no IaC/secrets scanning)
- Less mature ecosystem
Snyk
Pros:
- Excellent developer experience with fix suggestions
- IDE integrations
- Comprehensive language support
- Remediation advice
Cons:
- Commercial (free tier limited)
- Can be slower than Trivy/Grype
- Requires account/authentication
Severity Thresholds
Recommended Policy
# .trivy-policy.yaml
severity:
CRITICAL:
action: BLOCK
max_allowed: 0
notify:
- [email protected]
- slack:#security-alerts
HIGH:
action: WARN
max_allowed: 5
require_jira_ticket: true
MEDIUM:
action: WARN
max_allowed: 20
LOW:
action: ALLOW
report_only: true
ignore:
- CVE-2023-12345 # False positive - not applicable
- CVE-2023-67890 # Fix scheduled, approved exception
## Trivy Integration
### Basic Container Scanning
```bash
# Install Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
# Scan container image
trivy image --severity CRITICAL,HIGH nginx:latest
# Exit with error code if vulnerabilities found
trivy image --exit-code 1 --severity CRITICAL nginx:latest
# Generate report
trivy image --format json --output report.json nginx:latest
Filesystem Scanning
# Scan project dependencies
trivy fs --severity CRITICAL,HIGH .
# Scan specific package manager files
trivy fs --scanners vuln --severity CRITICAL package.json
trivy fs --scanners vuln --severity CRITICAL requirements.txt
trivy fs --scanners vuln --severity CRITICAL pom.xml
SBOM-Based Scanning
# Generate SBOM with Syft
syft nginx:latest -o cyclonedx-json > sbom.json
# Scan SBOM with Trivy
trivy sbom sbom.json --severity CRITICAL,HIGH
# This approach separates SBOM generation from vulnerability scanning
# Benefits: faster CI/CD, reusable SBOMs, audit trail
GitHub Actions Example
name: Vulnerability Gate
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail if vulnerabilities found
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
- name: Scan dependencies
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'table'
severity: 'CRITICAL'
exit-code: '1'
GitLab CI Example
vulnerability-gate:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- trivy fs --exit-code 1 --severity CRITICAL .
artifacts:
reports:
container_scanning: gl-container-scanning-report.json
only:
- merge_requests
- main
Grype Integration
Basic Usage
# Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Scan container image
grype nginx:latest
# Fail on high severity
grype nginx:latest --fail-on high
# Scan from SBOM
syft nginx:latest -o cyclonedx-json | grype
GitHub Actions with Grype
name: Grype Vulnerability Scan
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:test .
- name: Scan with Grype
uses: anchore/scan-action@v3
with:
image: myapp:test
fail-build: true
severity-cutoff: high
Advanced: Custom Vulnerability Database
For air-gapped environments or custom CVE tracking:
# Download Trivy vulnerability database
trivy image --download-db-only --cache-dir /tmp/trivy-cache
# Use offline database
trivy image --skip-db-update --cache-dir /tmp/trivy-cache nginx:latest
Handling False Positives
Trivy Ignore File
Create .trivyignore in your repo:
# False positive - vulnerability not applicable to our use case
CVE-2023-12345
# Acknowledged risk - approved by security team (JIRA: SEC-789)
CVE-2023-67890
# Waiting for upstream fix - temporary exception expires 2025-06-01
CVE-2024-11111
Policy-Based Exceptions
# trivy-policy.rego
package trivy
import rego.v1
# Ignore specific CVE in specific package
ignore contains result if {
input.PkgName == "spring-core"
input.VulnerabilityID == "CVE-2023-12345"
result := {"reason": "False positive - not using vulnerable code path"}
}
# Allow medium severity in dev environment
ignore contains result if {
input.Severity == "MEDIUM"
os.Getenv("ENVIRONMENT") == "dev"
result := {"reason": "Medium severity allowed in dev"}
}
Use with Trivy:
trivy image --ignore-policy trivy-policy.rego nginx:latest
Integration with SBOM Gates
Combine SBOM generation with vulnerability scanning:
#!/bin/bash
# combined-gate.sh
set -e
IMAGE="$1"
# Generate SBOM
echo "Generating SBOM..."
syft "$IMAGE" -o cyclonedx-json > sbom.json
# Upload SBOM to artifact repository
echo "Uploading SBOM to registry..."
curl -X POST -H "Content-Type: application/json" \
--data @sbom.json \
"https://sbom-registry.company.com/api/upload"
# Scan for vulnerabilities
echo "Scanning for vulnerabilities..."
trivy sbom sbom.json --severity CRITICAL,HIGH --exit-code 1
echo "✓ All gates passed"
Continuous Monitoring
Kubernetes Trivy Operator
For runtime vulnerability monitoring:
# Install Trivy Operator
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm install trivy-operator aqua/trivy-operator \
--namespace trivy-system --create-namespace
# View vulnerability reports
kubectl get vulnerabilityreports -A
kubectl describe vulnerabilityreport <report-name>
Alerting on New CVEs
# cronjob-vuln-monitor.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: vuln-monitor
spec:
schedule: "0 */6 * * *" # Every 6 hours
jobTemplate:
spec:
template:
spec:
containers:
- name: trivy
image: aquasec/trivy:latest
command:
- sh
- -c
- |
trivy image --severity CRITICAL myapp:production > /tmp/report.txt
if [ -s /tmp/report.txt ]; then
curl -X POST "$SLACK_WEBHOOK" \
-d "{\"text\":\"⚠️ New critical vulnerabilities detected in production\"}"
fi
env:
- name: SLACK_WEBHOOK
valueFrom:
secretKeyRef:
name: slack-webhook
key: url
restartPolicy: OnFailure
Best Practices
1. Scan at Multiple Stages
Development → Build → Deploy → Runtime
↓ ↓ ↓ ↓
IDE scan Image Registry Operator
scan scan monitoring
2. Progressive Enforcement
# Week 1-2: Monitor only (exit-code 0)
trivy image --severity CRITICAL nginx:latest
# Week 3-4: Block CRITICAL only
trivy image --exit-code 1 --severity CRITICAL nginx:latest
# Week 5+: Block CRITICAL and HIGH
trivy image --exit-code 1 --severity CRITICAL,HIGH nginx:latest
3. Dependency Pinning
Always pin exact versions to ensure reproducible builds:
# Bad: vulnerable to supply chain attacks
FROM node:18
# Good: pinned to specific digest
FROM node:18.19.0@sha256:a6385a6bb2fdcb7c48fc871e35e32af8daaa82c518900be49b76d10c005864c2
4. Automated Remediation
name: Auto-update Dependencies
on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update dependencies
run: |
npm update
npm audit fix
- name: Run vulnerability scan
run: trivy fs --exit-code 1 --severity CRITICAL .
- name: Create pull request
if: success()
uses: peter-evans/create-pull-request@v5
with:
title: "chore: Update dependencies (weekly)"
body: "Automated dependency updates passed vulnerability gates"
Metrics to Track
- Mean Time to Remediate (MTTR): Average time from CVE discovery to fix
- Vulnerability Debt: Total count of known vulnerabilities by severity
- Gate Block Rate: Percentage of builds blocked by vulnerability gates
- False Positive Rate: Percentage of flagged CVEs marked as not applicable
Troubleshooting
"Too Many Vulnerabilities" Error
Problem: Legacy application with hundreds of CVEs blocks all deployments.
Solution: Implement baseline and progressive reduction:
# Generate baseline
trivy image myapp:current --format json > baseline.json
# Only fail on NEW vulnerabilities
trivy image myapp:new --format json > current.json
diff baseline.json current.json || exit 1
Database Update Failures
Problem: Trivy fails to download vulnerability database in CI.
Solution: Cache the database:
- name: Cache Trivy DB
uses: actions/cache@v3
with:
path: ~/.cache/trivy
key: trivy-db-${{ github.run_id }}
restore-keys: trivy-db-
Performance Issues
Problem: Scans take too long in CI pipeline.
Solution: Use parallel scanning:
# Scan image and filesystem in parallel
trivy image myapp:test &
trivy fs . &
wait
Next Steps
- Compliance Gates - Enforce regulatory requirements
- CI/CD Integration - Complete pipeline examples
- Supply Chain Security - SBOM and artifact signing
Found an issue?