npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
The Common Vulnerability Scoring System (CVSS) is the industry standard framework maintained by FIRST (Forum of Incident Response and Security Teams) for assessing vulnerability severity. CVSS v4.0 (released November 2023) introduces refined metrics for more accurate scoring. This skill covers calculating CVSS scores, interpreting vector strings, and using CVSS alongside contextual factors like EPSS and CISA KEV for effective vulnerability prioritization.
When to Use
- When managing security operations that require prioritizing vulnerabilities with cvss scoring
- When improving security program maturity and operational processes
- When establishing standardized procedures for security team workflows
- When integrating threat intelligence or vulnerability data into operations
Prerequisites
- Understanding of common vulnerability types (buffer overflow, injection, XSS, etc.)
- Familiarity with networking concepts (attack vectors, protocols)
- Access to NVD (National Vulnerability Database) for CVE lookups
- Vulnerability scan results requiring prioritization
Core Concepts
CVSS v4.0 Metric Groups
1. Base Metrics (Intrinsic Severity)
Represent the inherent characteristics of a vulnerability:
Exploitability Metrics:
- Attack Vector (AV): Network (N), Adjacent (A), Local (L), Physical (P)
- Attack Complexity (AC): Low (L), High (H)
- Attack Requirements (AT): None (N), Present (P) - NEW in v4.0
- Privileges Required (PR): None (N), Low (L), High (H)
- User Interaction (UI): None (N), Passive (P), Active (A) - Expanded in v4.0
Impact Metrics (Vulnerable System):
- Confidentiality (VC): None (N), Low (L), High (H)
- Integrity (VI): None (N), Low (L), High (H)
- Availability (VA): None (N), Low (L), High (H)
Impact Metrics (Subsequent System):
- Confidentiality (SC): None (N), Low (L), High (H)
- Integrity (SI): None (N), Low (L), High (H)
- Availability (SA): None (N), Low (L), High (H)
2. Threat Metrics (Dynamic Context)
- Exploit Maturity (E): Attacked (A), POC (P), Unreported (U)
3. Environmental Metrics (Organization-Specific)
Modified versions of base metrics reflecting local deployment context, plus:
- Confidentiality Requirement (CR): High (H), Medium (M), Low (L)
- Integrity Requirement (IR): High (H), Medium (M), Low (L)
- Availability Requirement (AR): High (H), Medium (M), Low (L)
4. Supplemental Metrics (Advisory Information)
- Safety (S): Present (P), Negligible (X)
- Automatable (AU): Yes (Y), No (N)
- Recovery (R): Automatic (A), User (U), Irrecoverable (I)
- Value Density (V): Diffuse (D), Concentrated (C)
- Vulnerability Response Effort (RE): Low (L), Moderate (M), High (H)
- Provider Urgency (U): Red, Amber, Green, Clear
CVSS v4.0 Severity Ratings
| Score Range | Severity |
|---|---|
| 0.0 | None |
| 0.1 - 3.9 | Low |
| 4.0 - 6.9 | Medium |
| 7.0 - 8.9 | High |
| 9.0 - 10.0 | Critical |
CVSS v4.0 Vector String Format
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:NThis example represents a network-exploitable vulnerability requiring no privileges, no user interaction, no attack requirements, with high impact on confidentiality, integrity, and availability of the vulnerable system.
Workflow
Step 1: Assess Base Metrics
For each vulnerability, evaluate:
Example: CVE-2024-3094 (XZ Utils Backdoor)
Attack Vector: Network (N) - Exploitable over network
Attack Complexity: High (H) - Specific conditions required
Attack Requirements: Present (P) - Specific build/config needed
Privileges Required: None (N) - No authentication needed
User Interaction: None (N) - No victim action needed
Vulnerable System Impact:
Confidentiality: High (H) - Complete access to SSH keys
Integrity: High (H) - Arbitrary code execution
Availability: High (H) - Full system compromise
Subsequent System Impact:
Confidentiality: High (H) - Lateral movement possible
Integrity: High (H) - Network-wide compromise
Availability: None (N) - No downstream availability impact
Vector: CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:NStep 2: Apply Threat Intelligence Context
Enrich CVSS with real-world threat data:
Exploit Maturity: Attacked (A) - Active exploitation in the wild
EPSS Score: 0.94 - 94% probability of exploitation in 30 days
CISA KEV: Listed - Mandatory remediation for federal agenciesStep 3: Calculate Environmental Score
Adjust for organizational context:
Confidentiality Req: High (H) - Handles PII/financial data
Integrity Req: High (H) - Critical business process
Availability Req: Medium (M) - Has DR/failover capability
Modified Attack Vector: Network (N) - Internet-facing deploymentStep 4: Multi-Factor Prioritization Matrix
Combine CVSS with additional prioritization factors:
| Factor | Weight | Source |
|---|---|---|
| CVSS Base Score | 25% | NVD/Scanner |
| EPSS Score | 25% | FIRST EPSS API |
| Asset Criticality | 20% | Asset inventory/CMDB |
| CISA KEV Listed | 15% | CISA catalog |
| Network Exposure | 15% | Network segmentation data |
Step 5: Define Remediation SLAs
| Priority Level | CVSS Range | EPSS | Asset Tier | SLA |
|---|---|---|---|---|
| P1 - Emergency | 9.0-10.0 | >0.5 | Tier 1 | 24-48 hours |
| P2 - Critical | 7.0-8.9 | >0.3 | Tier 1-2 | 7 days |
| P3 - High | 7.0-8.9 | <0.3 | Tier 2-3 | 14 days |
| P4 - Medium | 4.0-6.9 | Any | Any | 30 days |
| P5 - Low | 0.1-3.9 | Any | Any | 90 days |
Best Practices
- Never rely solely on CVSS base score for prioritization
- Always incorporate threat intelligence (EPSS, KEV, exploit databases)
- Maintain accurate asset criticality ratings in your CMDB
- Adjust environmental metrics for your specific deployment context
- Use CVSS v4.0 vector strings for precise communication between teams
- Document scoring rationale for audit trail and consistency
- Re-evaluate scores when new threat intelligence becomes available
- Train remediation teams on interpreting CVSS metrics and vector strings
Common Pitfalls
- Treating CVSS base score as the sole prioritization factor
- Ignoring environmental metrics that reflect organizational risk
- Not updating threat metrics when exploit maturity changes
- Confusing CVSS severity with actual organizational risk
- Using outdated CVSS v2.0 scores instead of v3.1/v4.0
- Over-relying on scanner-provided scores without validation
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.9 KB
API Reference: Vulnerability Prioritization with CVSS Scoring
CVSS v3.1 Base Metrics
| Metric | Values | Description |
|---|---|---|
| AV (Attack Vector) | N, A, L, P | Network, Adjacent, Local, Physical |
| AC (Attack Complexity) | L, H | Low, High |
| PR (Privileges Required) | N, L, H | None, Low, High |
| UI (User Interaction) | N, R | None, Required |
| S (Scope) | U, C | Unchanged, Changed |
| C (Confidentiality) | N, L, H | None, Low, High |
| I (Integrity) | N, L, H | None, Low, High |
| A (Availability) | N, L, H | None, Low, High |
Severity Ratings
| Score Range | Rating |
|---|---|
| 0.0 | None |
| 0.1-3.9 | Low |
| 4.0-6.9 | Medium |
| 7.0-8.9 | High |
| 9.0-10.0 | Critical |
EPSS API (FIRST.org)
| Endpoint | Description |
|---|---|
GET https://api.first.org/data/v1/epss?cve=CVE-XXXX |
Get EPSS score |
Response: data[].epss |
Exploit probability (0-1) |
Response: data[].percentile |
Percentile ranking |
CISA KEV Catalog
| Field | Description |
|---|---|
| URL | https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json |
vulnerabilities[].cveID |
CVE identifier |
vulnerabilities[].dateAdded |
Date added to KEV |
vulnerabilities[].dueDate |
Remediation deadline |
Priority Scoring Formula
priority = cvss_score * 10
+ 30 if in CISA KEV
+ 20 if EPSS > 0.5
+ 10 if EPSS > 0.1Python Libraries
| Library | Version | Purpose |
|---|---|---|
requests |
>=2.28 | Fetch EPSS and KEV data |
math |
stdlib | CVSS score rounding |
json |
stdlib | Report generation |
References
- CVSS v3.1 Specification: https://www.first.org/cvss/specification-document
- EPSS Model: https://www.first.org/epss/
- CISA KEV: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- NVD: https://nvd.nist.gov/
standards.md1.7 KB
Standards and References - CVSS Scoring
Official CVSS Documentation
- CVSS v4.0 Specification: https://www.first.org/cvss/specification-document
- CVSS v4.0 User Guide: https://www.first.org/cvss/v4.0/user-guide
- CVSS v4.0 Calculator: https://www.first.org/cvss/calculator/4.0
- NVD CVSS v4 Calculator: https://nvd.nist.gov/vuln-metrics/cvss/v4-calculator
- Red Hat CVSS v4 Calculator: https://github.com/RedHatProductSecurity/cvss-v4-calculator
Complementary Scoring Systems
- EPSS (Exploit Prediction): https://www.first.org/epss/
- SSVC (Stakeholder-Specific): https://www.cisa.gov/ssvc
- CISA KEV Catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
Industry Standards
- NIST SP 800-40 Rev 4: Guide to Enterprise Patch Management Planning
- NIST NVD: National Vulnerability Database uses CVSS for all CVEs
- PCI DSS v4.0: Requires CVSS scoring for vulnerability prioritization
- ISO 27001:2022 A.8.8: Technical vulnerability management
CVSS v4.0 vs v3.1 Key Differences
| Feature | CVSS v3.1 | CVSS v4.0 |
|---|---|---|
| Metric Groups | 3 (Base, Temporal, Environmental) | 4 (Base, Threat, Environmental, Supplemental) |
| Attack Requirements | N/A | New metric (AT) |
| User Interaction | None/Required | None/Passive/Active |
| Scope | Changed/Unchanged | Replaced by Subsequent System metrics |
| Temporal -> Threat | Report Confidence, RL, E | Only Exploit Maturity |
| Supplemental | N/A | Safety, Automatable, Recovery, etc. |
Severity Thresholds
| Rating | CVSS v3.1 | CVSS v4.0 |
|---|---|---|
| None | 0.0 | 0.0 |
| Low | 0.1 - 3.9 | 0.1 - 3.9 |
| Medium | 4.0 - 6.9 | 4.0 - 6.9 |
| High | 7.0 - 8.9 | 7.0 - 8.9 |
| Critical | 9.0 - 10.0 | 9.0 - 10.0 |
workflows.md2.8 KB
Workflows - CVSS Vulnerability Prioritization
Workflow 1: Vulnerability Scoring Pipeline
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Receive CVE / │────>│ Look Up NVD │────>│ Extract CVSS │
│ Scan Results │ │ Base Score │ │ Vector String │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│
┌────────────────────────────────────────────────┘
v
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Enrich with EPSS │────>│ Check CISA KEV │────>│ Apply Env Metrics│
│ Threat Score │ │ Catalog │ │ (Asset Context) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│
v
┌──────────────────┐ ┌──────────────────┐
│ Calculate Final │────>│ Assign Priority │
│ Risk Score │ │ & SLA │
└──────────────────┘ └──────────────────┘Workflow 2: Risk-Weighted Prioritization
For each vulnerability:
1. base_score = NVD CVSS v4.0 base score (0-10)
2. epss_score = FIRST EPSS probability (0-1)
3. asset_score = CMDB criticality rating (1-5)
4. exposure_score = Network exposure (1=internal, 3=DMZ, 5=internet)
5. kev_bonus = 2.0 if in CISA KEV, else 0
risk_score = (base_score * 0.25) +
(epss_score * 10 * 0.25) +
(asset_score * 2 * 0.20) +
(exposure_score * 2 * 0.15) +
kev_bonus * 0.15
priority = map_to_sla(risk_score)Workflow 3: Continuous Re-Scoring
| Trigger | Action |
|---|---|
| New CVE published | Score with base metrics, schedule review |
| EPSS update (daily) | Refresh EPSS scores, re-prioritize |
| Exploit published | Update Exploit Maturity to POC or Attacked |
| Added to CISA KEV | Elevate priority to P1/P2 |
| Patch available | Add to remediation queue |
| Asset decommissioned | Close associated findings |
Scripts 2
agent.py5.9 KB
#!/usr/bin/env python3
"""Agent for prioritizing vulnerabilities with CVSS scoring.
Calculates CVSS v3.1 base scores from metric vectors, enriches
with EPSS exploit probability and KEV catalog data, and generates
a risk-prioritized remediation report.
"""
import json
import requests
from datetime import datetime
from collections import defaultdict
CVSS_WEIGHTS = {
"AV": {"N": 0.85, "A": 0.62, "L": 0.55, "P": 0.20},
"AC": {"L": 0.77, "H": 0.44},
"PR": {"N": {"U": 0.85, "C": 0.85}, "L": {"U": 0.62, "C": 0.68},
"H": {"U": 0.27, "C": 0.50}},
"UI": {"N": 0.85, "R": 0.62},
"S": {"U": "unchanged", "C": "changed"},
"C": {"H": 0.56, "L": 0.22, "N": 0.0},
"I": {"H": 0.56, "L": 0.22, "N": 0.0},
"A": {"H": 0.56, "L": 0.22, "N": 0.0},
}
class CVSSPrioritizationAgent:
"""Calculates CVSS scores and prioritizes vulnerabilities."""
def __init__(self):
self.vulnerabilities = []
def parse_vector(self, vector_string):
"""Parse CVSS v3.1 vector string into metric dict."""
metrics = {}
parts = vector_string.replace("CVSS:3.1/", "").split("/")
for part in parts:
key, val = part.split(":")
metrics[key] = val
return metrics
def calculate_base_score(self, vector_string):
"""Calculate CVSS v3.1 base score from vector string."""
m = self.parse_vector(vector_string)
scope_changed = m.get("S") == "C"
isc_base = 1 - (
(1 - CVSS_WEIGHTS["C"][m["C"]]) *
(1 - CVSS_WEIGHTS["I"][m["I"]]) *
(1 - CVSS_WEIGHTS["A"][m["A"]])
)
if scope_changed:
impact = 7.52 * (isc_base - 0.029) - 3.25 * (isc_base - 0.02) ** 15
else:
impact = 6.42 * isc_base
if impact <= 0:
return 0.0
scope_key = "C" if scope_changed else "U"
pr_val = CVSS_WEIGHTS["PR"][m["PR"]][scope_key]
exploitability = (8.22 * CVSS_WEIGHTS["AV"][m["AV"]] *
CVSS_WEIGHTS["AC"][m["AC"]] * pr_val *
CVSS_WEIGHTS["UI"][m["UI"]])
if scope_changed:
score = min(1.08 * (impact + exploitability), 10.0)
else:
score = min(impact + exploitability, 10.0)
import math
return math.ceil(score * 10) / 10
def severity_rating(self, score):
if score == 0.0:
return "None"
elif score <= 3.9:
return "Low"
elif score <= 6.9:
return "Medium"
elif score <= 8.9:
return "High"
return "Critical"
def fetch_epss(self, cve_ids):
"""Fetch EPSS exploit probability scores from FIRST.org API."""
scores = {}
try:
cves = ",".join(cve_ids[:100])
resp = requests.get(
f"https://api.first.org/data/v1/epss?cve={cves}", timeout=15)
if resp.status_code == 200:
for entry in resp.json().get("data", []):
scores[entry["cve"]] = {
"epss": float(entry.get("epss", 0)),
"percentile": float(entry.get("percentile", 0)),
}
except requests.RequestException:
pass
return scores
def fetch_kev(self):
"""Fetch CISA Known Exploited Vulnerabilities catalog."""
try:
resp = requests.get(
"https://www.cisa.gov/sites/default/files/feeds/"
"known_exploited_vulnerabilities.json", timeout=15)
if resp.status_code == 200:
return {v["cveID"] for v in
resp.json().get("vulnerabilities", [])}
except requests.RequestException:
pass
return set()
def add_vulnerability(self, cve_id, vector, description=""):
score = self.calculate_base_score(vector)
self.vulnerabilities.append({
"cve_id": cve_id, "vector": vector, "cvss_score": score,
"severity": self.severity_rating(score),
"description": description,
})
def prioritize(self):
"""Enrich and prioritize all vulnerabilities."""
cve_ids = [v["cve_id"] for v in self.vulnerabilities]
epss_data = self.fetch_epss(cve_ids)
kev_set = self.fetch_kev()
for vuln in self.vulnerabilities:
cve = vuln["cve_id"]
epss = epss_data.get(cve, {})
vuln["epss_score"] = epss.get("epss", 0)
vuln["epss_percentile"] = epss.get("percentile", 0)
vuln["in_kev"] = cve in kev_set
priority = vuln["cvss_score"] * 10
if vuln["in_kev"]:
priority += 30
if vuln["epss_score"] > 0.5:
priority += 20
elif vuln["epss_score"] > 0.1:
priority += 10
vuln["priority_score"] = round(priority, 1)
self.vulnerabilities.sort(key=lambda v: v["priority_score"], reverse=True)
return self.vulnerabilities
def generate_report(self):
self.prioritize()
sev_dist = defaultdict(int)
for v in self.vulnerabilities:
sev_dist[v["severity"]] += 1
report = {
"report_date": datetime.utcnow().isoformat(),
"total_vulns": len(self.vulnerabilities),
"severity_distribution": dict(sev_dist),
"kev_count": sum(1 for v in self.vulnerabilities if v["in_kev"]),
"prioritized_vulns": self.vulnerabilities,
}
print(json.dumps(report, indent=2))
return report
def main():
agent = CVSSPrioritizationAgent()
agent.add_vulnerability("CVE-2024-3400", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H")
agent.add_vulnerability("CVE-2024-21887", "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H")
agent.add_vulnerability("CVE-2023-44487", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H")
agent.generate_report()
if __name__ == "__main__":
main()
process.py15.0 KB
#!/usr/bin/env python3
"""
CVSS Vulnerability Prioritization Engine
Calculates CVSS v4.0 base scores, enriches with EPSS threat intelligence,
and generates risk-weighted prioritization for vulnerability remediation.
Requirements:
pip install requests pandas
Usage:
python process.py score --cve CVE-2024-3094
python process.py prioritize --csv vulns.csv --output prioritized.csv
python process.py enrich --csv vulns.csv
"""
import argparse
import json
import math
import sys
from datetime import datetime
import pandas as pd
import requests
class CVSSv4Calculator:
"""CVSS v4.0 Base Score Calculator."""
# CVSS v4.0 metric value mappings
METRIC_VALUES = {
"AV": {"N": 0.0, "A": 0.1, "L": 0.2, "P": 0.3},
"AC": {"L": 0.0, "H": 0.1},
"AT": {"N": 0.0, "P": 0.1},
"PR": {"N": 0.0, "L": 0.1, "H": 0.2},
"UI": {"N": 0.0, "P": 0.1, "A": 0.2},
"VC": {"H": 0.0, "L": 0.1, "N": 0.2},
"VI": {"H": 0.0, "L": 0.1, "N": 0.2},
"VA": {"H": 0.0, "L": 0.1, "N": 0.2},
"SC": {"H": 0.0, "L": 0.1, "N": 0.2},
"SI": {"H": 0.0, "L": 0.1, "N": 0.2},
"SA": {"H": 0.0, "L": 0.1, "N": 0.2},
}
# Severity thresholds
SEVERITY_RATINGS = [
(0.0, 0.0, "None"),
(0.1, 3.9, "Low"),
(4.0, 6.9, "Medium"),
(7.0, 8.9, "High"),
(9.0, 10.0, "Critical"),
]
@staticmethod
def parse_vector(vector_string: str) -> dict:
"""Parse a CVSS v4.0 vector string into metric components."""
metrics = {}
parts = vector_string.replace("CVSS:4.0/", "").replace("CVSS:3.1/", "").split("/")
for part in parts:
if ":" in part:
key, value = part.split(":", 1)
metrics[key] = value
return metrics
@staticmethod
def get_severity(score: float) -> str:
"""Map a numeric CVSS score to its severity rating."""
for low, high, rating in CVSSv4Calculator.SEVERITY_RATINGS:
if low <= score <= high:
return rating
return "Unknown"
@classmethod
def estimate_base_score(cls, vector_string: str) -> float:
"""
Estimate CVSS v4.0 base score from a vector string.
Note: Full CVSS v4.0 scoring uses complex lookup tables from FIRST.
This implements a simplified scoring approximation.
"""
metrics = cls.parse_vector(vector_string)
# Exploitability sub-score
av = cls.METRIC_VALUES["AV"].get(metrics.get("AV", "N"), 0)
ac = cls.METRIC_VALUES["AC"].get(metrics.get("AC", "L"), 0)
at = cls.METRIC_VALUES["AT"].get(metrics.get("AT", "N"), 0)
pr = cls.METRIC_VALUES["PR"].get(metrics.get("PR", "N"), 0)
ui = cls.METRIC_VALUES["UI"].get(metrics.get("UI", "N"), 0)
exploitability = 1.0 - (av + ac + at + pr + ui) / 1.0
# Vulnerable system impact
vc = cls.METRIC_VALUES["VC"].get(metrics.get("VC", "N"), 0.2)
vi = cls.METRIC_VALUES["VI"].get(metrics.get("VI", "N"), 0.2)
va = cls.METRIC_VALUES["VA"].get(metrics.get("VA", "N"), 0.2)
vuln_impact = 1.0 - (vc + vi + va) / 0.6
# Subsequent system impact
sc = cls.METRIC_VALUES["SC"].get(metrics.get("SC", "N"), 0.2)
si = cls.METRIC_VALUES["SI"].get(metrics.get("SI", "N"), 0.2)
sa = cls.METRIC_VALUES["SA"].get(metrics.get("SA", "N"), 0.2)
subseq_impact = 1.0 - (sc + si + sa) / 0.6
# Combined impact (weighted)
total_impact = 0.6 * vuln_impact + 0.4 * max(subseq_impact, 0)
if total_impact <= 0:
return 0.0
# Approximate base score
score = min(10.0, (exploitability * 4.0 + total_impact * 6.0))
return round(score, 1)
class VulnerabilityEnricher:
"""Enrich vulnerability data with EPSS scores and CISA KEV status."""
NVD_API = "https://services.nvd.nist.gov/rest/json/cves/2.0"
EPSS_API = "https://api.first.org/data/v1/epss"
CISA_KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"
def __init__(self):
self.kev_cache = None
self.session = requests.Session()
self.session.headers.update({"User-Agent": "CVSS-Prioritization-Tool/1.0"})
def get_nvd_data(self, cve_id: str) -> dict:
"""Fetch CVE details from NVD API v2.0."""
try:
response = self.session.get(
self.NVD_API, params={"cveId": cve_id}, timeout=30
)
if response.status_code == 200:
data = response.json()
vulns = data.get("vulnerabilities", [])
if vulns:
cve_data = vulns[0].get("cve", {})
metrics = cve_data.get("metrics", {})
# Try CVSS v4.0 first, then v3.1
cvss_data = {}
if "cvssMetricV40" in metrics:
m = metrics["cvssMetricV40"][0]["cvssData"]
cvss_data = {
"version": "4.0",
"vector": m.get("vectorString", ""),
"base_score": m.get("baseScore", 0),
"severity": m.get("baseSeverity", ""),
}
elif "cvssMetricV31" in metrics:
m = metrics["cvssMetricV31"][0]["cvssData"]
cvss_data = {
"version": "3.1",
"vector": m.get("vectorString", ""),
"base_score": m.get("baseScore", 0),
"severity": m.get("baseSeverity", ""),
}
descriptions = cve_data.get("descriptions", [])
desc = next(
(d["value"] for d in descriptions if d["lang"] == "en"),
""
)
return {
"cve_id": cve_id,
"description": desc[:300],
"published": cve_data.get("published", ""),
"cvss": cvss_data,
}
except Exception as e:
print(f" [!] NVD API error for {cve_id}: {e}")
return {}
def get_epss_score(self, cve_id: str) -> dict:
"""Fetch EPSS score for a CVE from FIRST API."""
try:
response = self.session.get(
self.EPSS_API, params={"cve": cve_id}, timeout=15
)
if response.status_code == 200:
data = response.json()
if data.get("data"):
entry = data["data"][0]
return {
"epss_score": float(entry.get("epss", 0)),
"epss_percentile": float(entry.get("percentile", 0)),
}
except Exception as e:
print(f" [!] EPSS API error for {cve_id}: {e}")
return {"epss_score": 0.0, "epss_percentile": 0.0}
def load_kev_catalog(self) -> set:
"""Load CISA Known Exploited Vulnerabilities catalog."""
if self.kev_cache is not None:
return self.kev_cache
try:
response = self.session.get(self.CISA_KEV_URL, timeout=30)
if response.status_code == 200:
data = response.json()
self.kev_cache = {
v["cveID"] for v in data.get("vulnerabilities", [])
}
print(f"[+] Loaded {len(self.kev_cache)} CVEs from CISA KEV catalog")
return self.kev_cache
except Exception as e:
print(f"[!] Failed to load CISA KEV: {e}")
self.kev_cache = set()
return self.kev_cache
def is_in_kev(self, cve_id: str) -> bool:
"""Check if a CVE is in the CISA KEV catalog."""
kev = self.load_kev_catalog()
return cve_id in kev
def enrich_cve(self, cve_id: str) -> dict:
"""Fully enrich a CVE with NVD, EPSS, and KEV data."""
result = {"cve_id": cve_id}
nvd = self.get_nvd_data(cve_id)
if nvd:
result.update({
"description": nvd.get("description", ""),
"published": nvd.get("published", ""),
"cvss_version": nvd.get("cvss", {}).get("version", ""),
"cvss_vector": nvd.get("cvss", {}).get("vector", ""),
"cvss_base_score": nvd.get("cvss", {}).get("base_score", 0),
"cvss_severity": nvd.get("cvss", {}).get("severity", ""),
})
epss = self.get_epss_score(cve_id)
result.update(epss)
result["in_cisa_kev"] = self.is_in_kev(cve_id)
return result
class VulnerabilityPrioritizer:
"""Risk-weighted vulnerability prioritization engine."""
def __init__(self, weights: dict = None):
self.weights = weights or {
"cvss": 0.25,
"epss": 0.25,
"asset_criticality": 0.20,
"kev": 0.15,
"exposure": 0.15,
}
def calculate_priority_score(self, vuln: dict) -> float:
"""Calculate composite priority score for a vulnerability."""
cvss_score = float(vuln.get("cvss_base_score", 0)) / 10.0
epss_score = float(vuln.get("epss_score", 0))
asset_crit = float(vuln.get("asset_criticality", 3)) / 5.0
kev_score = 1.0 if vuln.get("in_cisa_kev", False) else 0.0
exposure = float(vuln.get("exposure_score", 3)) / 5.0
priority = (
cvss_score * self.weights["cvss"] +
epss_score * self.weights["epss"] +
asset_crit * self.weights["asset_criticality"] +
kev_score * self.weights["kev"] +
exposure * self.weights["exposure"]
)
return round(priority * 10, 2)
def assign_sla(self, priority_score: float, cvss_score: float,
in_kev: bool = False) -> dict:
"""Assign remediation SLA based on priority score."""
if in_kev or priority_score >= 8.0:
return {"level": "P1-Emergency", "sla_days": 2, "sla": "48 hours"}
elif priority_score >= 6.5 or cvss_score >= 9.0:
return {"level": "P2-Critical", "sla_days": 7, "sla": "7 days"}
elif priority_score >= 5.0 or cvss_score >= 7.0:
return {"level": "P3-High", "sla_days": 14, "sla": "14 days"}
elif priority_score >= 3.0 or cvss_score >= 4.0:
return {"level": "P4-Medium", "sla_days": 30, "sla": "30 days"}
else:
return {"level": "P5-Low", "sla_days": 90, "sla": "90 days"}
def prioritize(self, vulnerabilities: list) -> pd.DataFrame:
"""Prioritize a list of vulnerabilities and return sorted DataFrame."""
results = []
for vuln in vulnerabilities:
score = self.calculate_priority_score(vuln)
sla = self.assign_sla(
score,
float(vuln.get("cvss_base_score", 0)),
vuln.get("in_cisa_kev", False)
)
results.append({
**vuln,
"priority_score": score,
"priority_level": sla["level"],
"sla_days": sla["sla_days"],
"remediation_sla": sla["sla"],
})
df = pd.DataFrame(results)
df = df.sort_values("priority_score", ascending=False)
return df
def main():
parser = argparse.ArgumentParser(description="CVSS Vulnerability Prioritization Engine")
subparsers = parser.add_subparsers(dest="command")
# Score a single CVE
score_parser = subparsers.add_parser("score", help="Score and enrich a single CVE")
score_parser.add_argument("--cve", required=True, help="CVE identifier (e.g., CVE-2024-3094)")
# Prioritize a CSV of vulnerabilities
pri_parser = subparsers.add_parser("prioritize", help="Prioritize vulnerabilities from CSV")
pri_parser.add_argument("--csv", required=True, help="Input CSV with cve_id column")
pri_parser.add_argument("--output", required=True, help="Output CSV with priorities")
# Enrich a CSV with EPSS/KEV data
enrich_parser = subparsers.add_parser("enrich", help="Enrich CVE list with EPSS and KEV")
enrich_parser.add_argument("--csv", required=True, help="Input CSV with cve_id column")
enrich_parser.add_argument("--output", default=None, help="Output enriched CSV")
args = parser.parse_args()
if args.command == "score":
enricher = VulnerabilityEnricher()
print(f"[*] Scoring {args.cve}...")
result = enricher.enrich_cve(args.cve)
print(f"\n{'='*60}")
print(f"CVE: {result.get('cve_id')}")
print(f"Description: {result.get('description', 'N/A')[:200]}")
print(f"Published: {result.get('published', 'N/A')}")
print(f"CVSS Version: {result.get('cvss_version', 'N/A')}")
print(f"CVSS Vector: {result.get('cvss_vector', 'N/A')}")
print(f"CVSS Base Score: {result.get('cvss_base_score', 'N/A')}")
print(f"CVSS Severity: {result.get('cvss_severity', 'N/A')}")
print(f"EPSS Score: {result.get('epss_score', 0):.4f} ({result.get('epss_percentile', 0)*100:.1f}th percentile)")
print(f"In CISA KEV: {'Yes' if result.get('in_cisa_kev') else 'No'}")
prioritizer = VulnerabilityPrioritizer()
priority = prioritizer.calculate_priority_score(result)
sla = prioritizer.assign_sla(
priority, float(result.get("cvss_base_score", 0)),
result.get("in_cisa_kev", False)
)
print(f"\nPriority Score: {priority}")
print(f"Priority Level: {sla['level']}")
print(f"Remediation SLA: {sla['sla']}")
elif args.command == "prioritize":
df = pd.read_csv(args.csv)
if "cve_id" not in df.columns:
print("[-] CSV must contain 'cve_id' column")
sys.exit(1)
enricher = VulnerabilityEnricher()
enriched = []
for _, row in df.iterrows():
cve = row["cve_id"]
print(f"[*] Processing {cve}...")
data = enricher.enrich_cve(cve)
data.update(row.to_dict())
enriched.append(data)
prioritizer = VulnerabilityPrioritizer()
result_df = prioritizer.prioritize(enriched)
result_df.to_csv(args.output, index=False)
print(f"\n[+] Prioritized results saved to: {args.output}")
print("\n=== Priority Summary ===")
print(result_df["priority_level"].value_counts().to_string())
elif args.command == "enrich":
df = pd.read_csv(args.csv)
enricher = VulnerabilityEnricher()
enriched = []
for _, row in df.iterrows():
cve = row.get("cve_id", "")
if cve:
print(f"[*] Enriching {cve}...")
data = enricher.enrich_cve(cve)
data.update(row.to_dict())
enriched.append(data)
result_df = pd.DataFrame(enriched)
output = args.output or args.csv.replace(".csv", "_enriched.csv")
result_df.to_csv(output, index=False)
print(f"[+] Enriched data saved to: {output}")
else:
parser.print_help()
if __name__ == "__main__":
main()