CVSS tells you how bad a vulnerability could be. It doesn’t tell you how bad it is for you.

Every security team I’ve worked with has the same problem: the vulnerability tracker has more items than people, the CVSS scores are all over the map, and management wants to know what to fix first. CVSS alone gives you a flat list of scary numbers. Priority requires context.

The Problem with CVSS-Only Triage

CVSS v3.1 measures exploitability and impact in a vacuum. A remote unauthenticated RCE in a widely-used library scores 10.0 regardless of whether anyone runs it, whether it’s internet-facing, or whether a patch exists. This means your CVSS-first tracker looks like this:

  • CVE-2024-XXXX — 10.0 — critical
  • CVE-2024-YYYY — 9.8 — critical
  • CVE-2024-ZZZZ — 8.1 — high
  • CVE-2024-AAAA — 7.5 — high

And so on. Ten “critical” items with identical urgency. Nobody can act on that.

The issue isn’t that CVSS is wrong. It’s that CVSS is generic. It describes the vulnerability, not your environment.

What Actually Drives Priority

Four signals matter more than CVSS alone:

1. Is it being exploited in the wild?

CISA’s Known Exploited Vulnerabilities (KEV) catalog is the best single signal for real-world exploitation. A vulnerability in KEV is actively being attacked. If you run the affected product, the vulnerability is no longer theoretical.

The difference between CVE-2021-44228 (Log4Shell) and CVE-2021-44226 was CVSS 10.0 vs. 7.5. In practice, CVE-2021-44226 got less attention despite being actively exploited because CVE-2021-44228 was a bigger headline. But KEV would have captured both equally.

2. What’s the probability of exploitation?

EPSS (Exploit Prediction Scoring System) gives a daily-updated probability — between 0 and 1 — that a CVE will be exploited in the wild within the next 30 days. It’s machine-learning-based, trained on thousands of features including CVSS, patch availability, mentioned-on-exploit-db, and more.

An EPSS of 0.95 means 95% probability of exploitation in the next month. An EPSS of 0.01 means 1%. CVSS doesn’t tell you this.

3. What asset is affected?

A CVSS 8.0 on your payment API is more urgent than a CVSS 10.0 on an internal wiki nobody uses. Asset criticality, internet exposure, and data sensitivity change the risk picture entirely.

4. Is there an easy exploit?

Public PoCs, Metasploit modules, and unauthenticated exploits shift a vulnerability from “theoretically bad” to “someone is scanning for it right now.” This matters especially for bug bounty targets and internet-facing services.

A Practical Prioritization Model

Here’s a weighted model I’ve used successfully. It takes five inputs and produces a single 0–100 priority score:

priority_score = (
    cvss_component × 0.25 +
    epss_component × 0.20 +
    kev_component × 0.20 +
    asset_component × 0.20 +
    exploit_maturity_component × 0.15
)

The Components

CVSS component (0–100): Normalize the CVSS base score. cvss × 10. Simple.

EPSS component (0–100): epss_probability × 100. If EPSS is unavailable, use 50 (unknown).

KEV component (0 or 100): In the KEV catalog? 100. Not? 0. Binary because KEV means confirmed exploitation.

Asset component (0–100): Combine three factors:

  • Asset criticality: low = 25, medium = 50, high = 75, critical = 100
  • Exposure multiplier: internal = 0.7, DMZ = 0.85, external = 1.0
  • Data sensitivity multiplier: public = 0.5, internal = 0.7, confidential = 0.9, restricted = 1.0

asset_score = criticality × exposure × data_sensitivity

Exploit maturity component (0–100):

  • Unproven: 20
  • Public PoC: 50
  • Functional exploit: 80
  • Widely exploited: 100
  • Public exploit available (regardless of maturity): minimum 60

Putting It Together

Let’s walk through two real-world examples.

Example A: CVE-2021-44228 (Log4Shell)

Component Value Weighted
CVSS (10.0) 100 25.0
EPSS (0.975) 97.5 19.5
KEV (yes) 100 20.0
Asset (critical × external × confidential = 100 × 1.0 × 0.9) 90 18.0
Exploit (functional/widely exploited) 100 15.0
Total 97.5

Priority: P1 — Critical. SLA: 24 hours.

Example B: A CVSS 9.1 SSRF in an internal tool

Component Value Weighted
CVSS (9.1) 91 22.75
EPSS (0.03) 3 0.6
KEV (no) 0 0.0
Asset (high × internal × internal = 75 × 0.7 × 0.7) 36.75 7.35
Exploit (public PoC) 50 7.5
Total 38.2

Priority: P3 — Medium. SLA: 30 days.

The internal SSRF has a higher CVSS but lower priority than Log4Shell. And more importantly, it’s lower priority than some other CVEs that might have a CVSS of 8.5 but are in KEV and run on an external-facing critical asset.

Mapping Priority to SLAs

Once you have the score, map it to action:

Score Priority SLA
80+ P1 — Critical 24 hours
60–79 P2 — High 7 days
40–59 P3 — Medium 30 days
0–39 P4 — Low 90 days

These are starting points, not laws. Adjust based on your team’s capacity and business context. The point is consistency: the same formula applied to every vulnerability, every time.

Getting the Data

CVSS: NVD API (https://services.nvd.nist.gov/rest/json/cves/2.0). Free with API key (rate limit: 50 req/30s without key, higher with key).

EPSS: EPSS API (https://api.first.org/data/v1/epss). Free, no key needed. Supports bulk queries (comma-separated CVE IDs).

KEV: CISA’s catalog (https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json). JSON feed, update monthly or check on demand.

Asset context: Your CMDB, asset inventory, or whatever spreadsheet you maintain. Needs: asset name, criticality rating, exposure (internal/external), data classification.

Automation Tips

  1. Run the calculation daily. EPSS scores change. New KEV entries appear monthly. Vulnerabilities age.
  2. Keep it in your ticketing system. Don’t maintain a separate spreadsheet. Feed the priority score into Jira, GitHub Issues, or your tracker.
  3. Filter before you triage. If you’re getting 500 new CVEs a month, cap your triage to items scoring above 40. Below that is noise unless you have a specific reason to care.
  4. Review the formula quarterly. Adjust weights based on what your team actually encounters. If EPSS consistently over-predicts, reduce its weight. If KEV entries consistently beat EPSS in actual exploitation, increase KEV’s weight.

When CVSS Still Matters

CVSS isn’t useless. It’s good for:

  • Comparing vulnerabilities in the same product — when asset context is identical, CVSS is a decent ranking signal.
  • Communicating with non-technical stakeholders — “CVSS 9.4” means something to executives who’ve seen it before.
  • Scoring the vulnerability itself — if you’re a vendor and need to communicate severity to users.

But for prioritization within your environment, CVSS is just one input — and often not the most important one.

The Real Lesson

The best vulnerability prioritization framework isn’t the most complex one. It’s the one your team actually uses consistently. Start simple:

  1. Is it in KEV? → Fix it now.
  2. Does it affect an external-facing critical asset with a public exploit? → Fix it this week.
  3. Everything else → Rank by CVSS, review monthly.

Add EPSS when you’re ready. Add asset scoring when your CMDB is decent. Add exploit maturity when your triage process has the bandwidth.

The goal isn’t perfect prioritization. It’s consistent prioritization that improves over time.

Further Reading