| name | vulnerability-scan |
| description | Implement automated vulnerability scanning for containers, dependencies, and infrastructure. Outputs Trivy/Grype configuration, CVE triage workflows, remediation SLAs, and compliance reporting. |
| argument-hint | ["infrastructure type","compliance requirements","languages/runtimes","container registry"] |
| allowed-tools | Read, Write, Bash |
Vulnerability Scanning
Automated vulnerability scanning finds known CVEs in your dependencies, containers, and infrastructure before attackers do. The goal is not zero vulnerabilities — it is systematic triage, prioritization, and remediation within defined SLAs.
Process
- Define scope — application dependencies, OS packages, container images, IaC.
- Choose scanners — Trivy (container + IaC), Grype (application), Dependabot (code), Checkov (IaC).
- Integrate into CI — scan on every build, block on critical CVEs.
- Set severity thresholds — which severities block CI vs. create tickets.
- Triage findings — assess exploitability, determine if false positive.
- Remediation SLAs — CRITICAL 24h, HIGH 7d, MEDIUM 30d, LOW 90d.
- Track and report — vulnerability dashboard, trend over time.
Output Format
Trivy Configuration
CVE-2023-44487
CVE-2023-1234
---
vulnerability:
type: [os, library]
ignore-unfixed: false
severity: [CRITICAL, HIGH, MEDIUM, LOW]
format: json
skip-dirs:
- vendor/
- node_modules/
- .git/
skip-files:
- "**/testdata/**"
secret:
config: trivy-secret.yaml
misconfig:
include-non-failures: false
Container Scanning
#!/bin/bash
IMAGE=$1
SEVERITY_THRESHOLD="${2:-HIGH}"
echo "Scanning image: $IMAGE"
trivy image \
--format json \
--output scan-results.json \
--severity CRITICAL,HIGH,MEDIUM,LOW \
--ignore-unfixed \
"$IMAGE"
CRITICAL=$(jq '[.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL")] | length' scan-results.json)
HIGH=$(jq '[.Results[].Vulnerabilities[] | select(.Severity == "HIGH")] | length' scan-results.json)
MEDIUM=$(jq '[.Results[].Vulnerabilities[] | select(.Severity == "MEDIUM")] | length' scan-results.json)
echo "Results: CRITICAL=$CRITICAL HIGH=$HIGH MEDIUM=$MEDIUM"
trivy image --severity CRITICAL,HIGH "$IMAGE"
if [ "$CRITICAL" -gt 0 ]; then
echo "❌ CRITICAL vulnerabilities found — blocking deployment"
jq '.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL") | {CVE: .VulnerabilityID, Package: .PkgName, Version: .InstalledVersion, Fix: .FixedVersion, Title: .Title}' scan-results.json
exit 1
fi
if [ "$HIGH" -gt 0 ] && [ "$SEVERITY_THRESHOLD" = "HIGH" ]; then
echo
0
CI Pipeline Integration
name: Vulnerability Scanning
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 8 * * *'
jobs:
dependency-scan:
name: Dependency Vulnerabilities
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Safety (Python)
run: |
pip install safety
safety check \
--output json \
--file requirements.txt \
> safety-report.json || true
python -c "
import json, sys
data = json.load(open('safety-report.json'))
vulns = data.get('vulnerabilities', [])
critical = [v for v in vulns if v.get('severity', '').lower() == 'critical']
print(f'Found {len(vulns)} vulnerabilities ({len(critical)} critical)')
if critical:
for v in critical:
print(f' CRITICAL: {v[\"package_name\"]} {v[\"analyzed_version\"]} - {v[\"advisory\"]}')
sys.exit(1)
"
- name: npm audit
run: |
npm audit --json > npm-audit.json || true
node -e "
const data = require('./npm-audit.json');
const critical = data.vulnerabilities ?
Object.values(data.vulnerabilities).filter(v => v.severity === 'critical') : [];
console.log(\`npm: \${critical.length} critical vulnerabilities\`);
if (critical.length > 0) process.exit(1);
"
[]
CVE Triage Workflow
import json
from dataclasses import dataclass
from typing import Optional
from enum import Enum
import requests
class Decision(Enum):
FIX = "fix"
ACCEPT = "accept"
FALSE_POSITIVE = "false_positive"
WONT_FIX = "wont_fix"
@dataclass
class CVETriage:
cve_id: str
package: str
severity: str
decision: Decision
rationale: str
jira_ticket: Optional[str] = None
suppression_expiry: Optional[str] = None
class CVETriager:
def __init__(self, nvd_api_key: str = None):
self.nvd_api_key = nvd_api_key
def get_cvss_details(self, cve_id: str) -> dict:
"""Fetch CVSS details from NVD API."""
url =
headers = {}
.nvd_api_key:
headers[] = .nvd_api_key
response = requests.get(url, headers=headers, timeout=)
response.status_code == :
data = response.json()
data.get():
vuln = data[][][]
metrics = vuln.get(, {}).get(, [{}])[]
{
: vuln.get(, [{}])[].get(, ),
: metrics.get(, {}).get(),
: metrics.get(, {}).get(),
: metrics.get(, {}).get(),
}
{}
() -> :
details = .get_cvss_details(cve_id)
network_exploitable = details.get() ==
auth_required = details.get() !=
risk_multiplier =
context.get(, ):
risk_multiplier *=
context.get(, ):
risk_multiplier *=
auth_required context.get(, ):
risk_multiplier *=
{
: details.get(),
: details.get(, ) * risk_multiplier,
: risk_multiplier > ,
: ,
}
():
results = json.loads(scan_json)
triage_items = []
result results.get(, []):
vuln result.get(, []):
severity = vuln.get(, )
cve_id = vuln.get()
fix_version = vuln.get(, )
item = {
: cve_id,
: ,
: severity,
: (fix_version),
: fix_version,
: vuln.get(, ),
: {: , : , : , : }.get(severity, ),
}
triage_items.append(item)
report = {
: datetime.now(timezone.utc).isoformat(),
: (triage_items),
: {
s: ([i i triage_items i[] == s])
s [, , , ]
},
: ([i i triage_items i[]]),
: triage_items,
}
(output_path, ) f:
json.dump(report, f, indent=)
report
Remediation Tracking
# Vulnerability Remediation Tracker
## Active Vulnerabilities
| CVE | Package | Severity | Found | Fix By | Ticket | Status |
|-----|---------|----------|-------|--------|--------|--------|
| CVE-2024-1234 | libcurl 7.x | CRITICAL | 2024-01-15 | 2024-01-16 | SECU-101 | In Progress |
| CVE-2024-5678 | openssl 1.x | HIGH | 2024-01-10 | 2024-01-17 | SECU-98 | Fixed |
| CVE-2024-9012 | pyjwt 2.4 | MEDIUM | 2024-01-05 | 2024-02-05 | SECU-95 | Accepted |
## Accepted Risk Register
| CVE | Rationale | Accepted By | Review Date |
|-----|-----------|-------------|-------------|
| CVE-2023-44487 | HTTP/2 rapid reset: mitigated at load balancer | CISO | 2024-04-01 |
## SLA Policy
- CRITICAL: Fix within 24 hours
- HIGH: Fix within 7 days
- MEDIUM: Fix within 30 days
- LOW: Fix within 90 days or accept with documentation
Rules
- Scan every image push — not just release builds; vulnerabilities enter through dev images too.
- Block CRITICAL in CI — a CRITICAL CVE with a known exploit should never deploy.
- Track accepted risks — undocumented suppressions are technical debt that grows silently.
- SBOM for every release — Software Bill of Materials enables rapid response to zero-days.
- Suppress with expiry dates — force re-evaluation of accepted risks on a schedule.
- Prioritize by exploitability, not CVSS score — a CRITICAL CVE in unreachable code is lower risk than a HIGH CVE in your login endpoint.
- Fix the base image, not just the app — OS-level vulnerabilities require image rebuild.
- Dependency pinning with lock files — unpinned dependencies silently introduce new vulnerabilities.
- Monitor for zero-days between scans — subscribe to NVD alerts for your key dependencies.
- Measure mean time to remediation — track and trend this metric to demonstrate improvement.