Skip to main content

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

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

# .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

Found an issue?