Install
openclaw skills install @1beekeeper/cve-trackerAutomated CVE monitoring and alerting for tech stacks — NVD/NIST API integration, CVE-MCP enrichment, EPSS scoring, KEV catalog tracking. Daily digests with prioritized remediation.
openclaw skills install @1beekeeper/cve-trackerAutomated CVE monitoring and alerting system for technology stacks. Continuously tracks the NVD/NIST database, enriches findings with EPSS scoring and CISA KEV catalog data via CVE-MCP, and delivers prioritized daily digests. Designed for security teams who need to know about relevant vulnerabilities before attackers do.
Runs on ARGUS infrastructure with CVE-MCP integration.
curl, jq on PATH| Component | Location | Purpose |
|---|---|---|
| NVD API 2.0 | services.nvd.nist.gov | Primary CVE data source |
| CVE-MCP | localhost MCP | CVE enrichment + EPSS + KEV |
| ARGUS cron | local | Scheduled monitoring jobs |
Create a tech stack inventory that the tracker monitors:
cat > /tmp/tech-stack.json << 'EOF'
{
"stack_name": "production-infrastructure",
"last_updated": "2026-07-04",
"products": [
{"name": "nginx", "version": "1.24.0", "category": "web-server"},
{"name": "postgresql", "version": "15.3", "category": "database"},
{"name": "redis", "version": "7.0.11", "category": "cache"},
{"name": "nodejs", "version": "20.11.0", "category": "runtime"},
{"name": "openssl", "version": "3.0.8", "category": "crypto"},
{"name": "kubernetes", "version": "1.28.0", "category": "orchestration"},
{"name": "docker", "version": "24.0.5", "category": "container"},
{"name": "python", "version": "3.11.4", "category": "runtime"},
{"name": "gitlab", "version": "16.8.0", "category": "devops"},
{"name": "haproxy", "version": "2.8.1", "category": "load-balancer"}
],
"watchlist": [
"openssl", "nginx", "postgresql", "kubernetes", "docker"
],
"ignore_cve": []
}
EOF
Check CVEs for one product via CVE-MCP:
PRODUCT="openssl"
VERSION="3.0.8"
echo "=== CVE Check: $PRODUCT $VERSION ==="
curl -s -X POST "http://localhost:8765/cve-mcp/search" \
-H "Content-Type: application/json" \
-d "{\"product\": \"$PRODUCT\", \"version\": \"$VERSION\"}" | \
jq '.[] | {
cve: .id,
cvss: .cvss_score,
severity: .severity,
published: .published_date,
kev: .cisa_kev,
epss: .epss_score,
exploit_available: .exploit_available,
fixed_in: .fixed_version,
summary: .description[:120]
}' | head -60
Scan entire tech stack and produce a prioritized report:
STACK_FILE="/tmp/tech-stack.json"
REPORT_DIR="$HOME/App/domains/argus/reports/cve-tracker"
REPORT_FILE="$REPORT_DIR/cve-report-$(date +%Y%m%d-%H%M%S).json"
mkdir -p "$REPORT_DIR"
echo "=== CVE Tracker: Bulk Tech Stack Scan ==="
echo "Stack: $(jq -r '.stack_name' "$STACK_FILE")"
echo "Products: $(jq '.products | length' "$STACK_FILE")"
echo ""
# Initialize report
echo '{"scan_date": "'$(date -Iseconds)'", "findings": []}' > "$REPORT_FILE"
# Scan each product
jq -c '.products[]' "$STACK_FILE" | while IFS= read -r product; do
name=$(echo "$product" | jq -r '.name')
version=$(echo "$product" | jq -r '.version')
echo "--- $name $version ---"
result=$(curl -s -X POST "http://localhost:8765/cve-mcp/search" \
-H "Content-Type: application/json" \
-d "{\"product\": \"$name\", \"version\": \"$version\"}" 2>/dev/null)
if [ -n "$result" ] && [ "$result" != "null" ] && [ "$result" != "[]" ]; then
cve_count=$(echo "$result" | jq 'length')
echo " Found: $cve_count CVEs"
# Prioritize: KEV + EPSS first
echo "$result" | jq -c --arg product "$name" --arg version "$version" '.[] | {
product: $product,
version: $version,
cve: .id,
cvss: .cvss_score,
severity: .severity,
kev: .cisa_kev,
epss: .epss_score,
exploit: .exploit_available,
fixed: .fixed_version
}' >> "$REPORT_DIR/tmp-findings.jsonl"
else
echo " No CVEs found"
fi
done
# Compile final report
if [ -f "$REPORT_DIR/tmp-findings.jsonl" ]; then
echo ""
echo "=== Prioritized Findings ==="
# Sort by severity: KEV first, then CVSS descending
jq -s 'sort_by(
(if .kev == true then 0 else 1 end),
(if .cvss then -.cvss else 999 end)
)' "$REPORT_DIR/tmp-findings.jsonl" > "$REPORT_FILE.tmp"
# Move to final report
jq --argjson findings "$(cat "$REPORT_FILE.tmp")" \
'.findings = $findings' "$REPORT_FILE" > "${REPORT_FILE}.final"
mv "${REPORT_FILE}.final" "$REPORT_FILE"
# Print summary
total=$(jq '.findings | length' "$REPORT_FILE")
critical=$(jq '[.findings[] | select(.cvss >= 9.0)] | length' "$REPORT_FILE")
high=$(jq '[.findings[] | select(.cvss >= 7.0 and .cvss < 9.0)] | length' "$REPORT_FILE")
kev=$(jq '[.findings[] | select(.kev == true)] | length' "$REPORT_FILE")
with_exploit=$(jq '[.findings[] | select(.exploit == true)] | length' "$REPORT_FILE")
echo ""
echo "Summary:"
echo " Total CVEs: $total"
echo " Critical (≥9.0): $critical"
echo " High (7.0-8.9): $high"
echo " CISA KEV: $kev"
echo " Exploit available: $with_exploit"
echo ""
echo "Report: $REPORT_FILE"
# Show top 5 most critical
echo ""
echo "=== Top 5 Most Critical ==="
jq -r '.findings[:5][] | " \(.cve) | \(.product) \(.version) | CVSS:\(.cvss) | KEV:\(.kev) | Exploit:\(.exploit)"' "$REPORT_FILE"
rm -f "$REPORT_DIR/tmp-findings.jsonl" "$REPORT_FILE.tmp"
fi
Fetch recently published CVEs directly from NVD:
# Last 24 hours, moderate+ severity
NVD_API="https://services.nvd.nist.gov/rest/json/cves/2.0"
curl -s "${NVD_API}?pubStartDate=$(date -d '24 hours ago' -Iseconds | sed 's/+.*//')&pubEndDate=$(date -Iseconds | sed 's/+.*//')&cvssV3Severity=CRITICAL&resultsPerPage=20" | \
jq '.vulnerabilities[] | {
cve: .cve.id,
published: .cve.published,
cvss: .cve.metrics.cvssMetricV31[0].cvssData.baseScore,
severity: .cve.metrics.cvssMetricV31[0].cvssData.baseSeverity,
vector: .cve.metrics.cvssMetricV31[0].cvssData.vectorString,
description: .cve.descriptions[0].value[:150]
}'
Check CISA Known Exploited Vulnerabilities catalog for your products:
STACK_FILE="/tmp/tech-stack.json"
echo "=== CISA KEV Catalog Check ==="
# Get watchlist products
jq -r '.watchlist[]' "$STACK_FILE" | while IFS= read -r product; do
echo "--- $product ---"
curl -s -X POST "http://localhost:8765/cve-mcp/search" \
-H "Content-Type: application/json" \
-d "{\"product\": \"$product\", \"kev_only\": true}" | \
jq -r '.[] | " \(.id) — \(.description[:100])"'
done
Get EPSS (Exploit Prediction Scoring System) scores:
CVE="CVE-2024-3094"
curl -s -X POST "http://localhost:8765/cve-mcp/epss" \
-H "Content-Type: application/json" \
-d "{\"cve_id\": \"$CVE\"}" | jq '{
cve: .cve_id,
epss_score: .epss,
percentile: .percentile,
date: .date,
interpretation: (
if .epss >= 0.5 then "HIGH — likely exploited within 30 days"
elif .epss >= 0.1 then "MEDIUM — moderate exploitation probability"
else "LOW — unlikely to be exploited"
end
)
}'
Set up a daily CVE digest as an ARGUS cron job:
# This would be registered as a cron job in ~/.hermes/cron/
# Command to run daily:
DAILY_DIGEST_SCRIPT="$HOME/App/domains/argus/clawhub-skills/cve-tracker/daily-digest.sh"
cat > "$DAILY_DIGEST_SCRIPT" << 'CRONEOF'
#!/bin/bash
STACK_FILE="$HOME/App/domains/argus/clawhub-skills/cve-tracker/tech-stack.json"
DIGEST_DIR="$HOME/App/domains/argus/reports/cve-tracker/digests"
mkdir -p "$DIGEST_DIR"
DIGEST="$DIGEST_DIR/digest-$(date +%Y%m%d).md"
echo "# CVE Daily Digest — $(date +%Y-%m-%d)" > "$DIGEST"
echo "" >> "$DIGEST"
# Check each product in watchlist
jq -r '.watchlist[]' "$STACK_FILE" | while IFS= read -r product; do
result=$(curl -s -X POST "http://localhost:8765/cve-mcp/search" \
-H "Content-Type: application/json" \
-d "{\"product\": \"$product\", \"days\": 7}" 2>/dev/null)
count=$(echo "$result" | jq 'length' 2>/dev/null || echo "0")
echo "## $product — $count new CVEs this week" >> "$DIGEST"
if [ "$count" -gt 0 ]; then
echo "$result" | jq -r '.[] |
"- **\(.id)** (CVSS \(.cvss_score)): \(.description[:100])\n KEV: \(.cisa_kev) | EPSS: \(.epss_score) | Exploit: \(.exploit_available)"
' >> "$DIGEST" 2>/dev/null
fi
echo "" >> "$DIGEST"
done
echo "Digest written: $DIGEST"
CRONEOF
chmod +x "$DAILY_DIGEST_SCRIPT"
echo "Daily digest script created: $DAILY_DIGEST_SCRIPT"
When onboarding a new system:
Day-to-day CVE tracking:
When a high-profile CVE drops (e.g., Log4Shell, xz backdoor):
| Variable | Default | Description |
|---|---|---|
CVE_MCP_URL | http://localhost:8765/cve-mcp | CVE-MCP endpoint |
NVD_API_URL | https://services.nvd.nist.gov/rest/json/cves/2.0 | NVD API 2.0 endpoint |
TECH_STACK_FILE | ./clawhub-skills/cve-tracker/tech-stack.json | Product inventory |
DIGEST_DIR | ~/App/domains/argus/reports/cve-tracker/digests | Daily digest output |
DIGEST_CRON_SCHEDULE | 0 8 * * * | Daily digest schedule (8 AM) |
ALERT_THRESHOLD_CVSS | 9.0 | Minimum CVSS for immediate alert |
ALERT_WEBHOOK | (optional) | Slack/Discord webhook for critical alerts |
WATCHLIST_ONLY | false | Only monitor watchlist products |
SCAN_DAYS_BACK | 7 | Days to look back for new CVEs |
| Symptom | Likely Cause | Fix |
|---|---|---|
| NVD API returns empty | Rate limiting | NVD API allows ~5 req/30s without key; add apiKey param |
| CVE-MCP returns null | MCP server down | curl http://localhost:8765/cve-mcp/health |
| jq parse errors | Empty API response | Add // empty to jq filters for null safety |
| Daily digest empty | No new CVEs this week | Normal — check tech stack file for correctness |
| EPSS scores missing | EPSS not in CVE-MCP response | EPSS only available for CVEs with CVSS v3 scores |
| Too many results | Broad product match | Use specific version in tech stack, not wildcards |
CVE-MCP provides the intelligence layer: