Vulnerability Gates
Automated vulnerability scanning and threshold enforcement in CI/CD pipelines
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:
- security-team@company.com
- 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?