vulnerability management

Prioritizing Vulnerabilities with CVSS Scoring

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.

cvecvssnistprioritizationriskvulnerability-management
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

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:N

This 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:N

Step 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 agencies

Step 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 deployment

Step 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

  1. Never rely solely on CVSS base score for prioritization
  2. Always incorporate threat intelligence (EPSS, KEV, exploit databases)
  3. Maintain accurate asset criticality ratings in your CMDB
  4. Adjust environmental metrics for your specific deployment context
  5. Use CVSS v4.0 vector strings for precise communication between teams
  6. Document scoring rationale for audit trail and consistency
  7. Re-evaluate scores when new threat intelligence becomes available
  8. 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
Source materials

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

Python Libraries

Library Version Purpose
requests >=2.28 Fetch EPSS and KEV data
math stdlib CVSS score rounding
json stdlib Report generation

References

standards.md1.7 KB

Standards and References - CVSS Scoring

Official CVSS Documentation

Complementary Scoring Systems

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
Display-only source. This catalog never executes bundled scripts.
#!/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
Display-only source. This catalog never executes bundled scripts.
#!/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()

Assets 1

template.mdtext/markdown · 1.4 KB
Keep exploring