Back to glossary

Vulnerability Prioritization

Vulnerability prioritization ranks vulnerabilities by the risk each poses in your system, using reachability, exploitation, EPSS, and CVSS, not severity alone.

Also known as risk-based vulnerability management, RBVM, risk-based prioritization, contextual vulnerability prioritization, CVE prioritization

Vulnerability prioritization is the practice of ranking known vulnerabilities by the risk each one poses in your specific system, not by severity score alone, so scarce remediation effort lands on the few that matter. It combines reachability analysis, known exploitation (CISA KEV), exploit probability (EPSS), and CVSS severity into one ordered queue.

We ran npm audit on a nine-package lockfile and got 77 distinct CVE advisories. CVSS rates 38 of them high or critical. Four carry an EPSS probability above 0.10. Zero appear in the CISA KEV catalog. Three signals, three incompatible answers about what to fix. Our position: gate on reachability first, treat known exploitation as a hard override, sort the remainder by EPSS, and use CVSS only to break ties.

What is vulnerability prioritization?

#

Vulnerability prioritization is the decision stage of risk-based vulnerability management: the step that takes a flat list of findings and puts it in the order you should fix them. Vulnerability scanning produces the inventory by matching your software against CVE records. Prioritization decides which of those findings is worth an engineer's time this week.

The step exists because the inventory is enormous and the capacity to fix is not. 48,185 CVEs were published in 2025, and submissions keep climbing: NIST cited a 263% rise between 2020 and 2025 when it stopped enriching most records. A 2025 survey of 82 studies on vulnerability prioritization sorts the signals into five families: severity, exploitability, contextual, predictive, and aggregated. Four of them do the real work, and they disagree sharply about the same findings.

The figure shows one npm lockfile's 77 CVE advisories filtered by each signal on October 20, 2026. CVSS 7.0 or higher keeps 38. An EPSS probability of 0.10 or higher keeps 4: the mongoose, lodash, qs, and moment advisories. CISA KEV keeps 0. The three queues barely overlap, which is the whole problem: pick the wrong signal and you spend the week on the wrong 38.

Bash
$ cat package.json | python3 -c 'import json,sys;print(len(json.load(sys.stdin)["dependencies"]))'
9

$ npm install --package-lock-only --ignore-scripts
$ npm audit --json | python3 -c 'import json,sys;print(json.load(sys.stdin)["metadata"]["vulnerabilities"])'
{'info': 0, 'low': 4, 'moderate': 3, 'high': 15, 'critical': 1, 'total': 23}

npm audit counts 23 vulnerable packages. Expanded to one row per advisory, that is 77 CVEs across direct and transitive dependencies. The counting unit alone changes the number a dashboard reports by 3x, before anyone has ranked anything.

Why does a CVSS score rank vulnerabilities poorly?

#

A CVSS base score ranks vulnerabilities poorly because it describes the flaw in the abstract and knows nothing about your deployment. FIRST, which maintains CVSS, says the base score is meant to be refined by temporal and environmental metrics, not used alone. Two failures show up constantly in open source dependencies: context blindness and identity drift.

Context blindness: CVE-2026-25645 flags every project on requests below 2.33.0. The maintainers' advisory narrows it to almost nobody. “Standard usage of the Requests library is not affected by this vulnerability,” it states, and only code that calls requests.utils.extract_zipped_paths() directly is exposed. A CVSS number cannot tell you whether your code reaches that function; reachability analysis can.

Identity drift: scanners disagree about the same CVE because they read different records of it. The NVD and the GitHub Advisory Database assign conflicting CVSS scores to roughly 1,500 CVEs, with gaps as wide as 6.9 points, enough to move a finding from “next month” to “tonight.” Worse, one mongoose prototype-pollution flaw in our lockfile exists under two different CVE IDs with two different scores.

Bash
$ curl -s https://cveawg.mitre.org/api/cve/CVE-2022-24304 \
    | python3 -c 'import json,sys;d=json.load(sys.stdin);print(d["cveMetadata"]["state"])'
REJECTED
# "This candidate is a duplicate of CVE-2022-2564."

CVE-2022-24304 was rejected as a duplicate of CVE-2022-2564. The two describe the same mongoose Schema.path() bug. A scanner keyed on the dead ID either double-counts it or drops it, and CVE-2022-2564 happens to carry the highest EPSS score in the whole lockfile (0.313, the 98th percentile). The highest-value finding is the one most likely to be mislabeled.

How do you prioritize vulnerability scan results?

#

Prioritize vulnerability scan results by applying the cheapest decisive signal first and stopping as soon as a finding is settled. The order that works is reachability, then known exploitation, then exploit probability, then severity.

Gate on reachability. If your code cannot reach the vulnerable function, the CVE cannot hurt you, whatever its score. This removes the most findings, so it goes first.

Override on known exploitation. If a reachable CVE is in CISA KEV, fix it now. KEV lists only CVEs with confirmed exploitation in the wild: 1,754 of them as of catalog version 2026.10.20, under 0.4% of a single year's CVEs. Membership is rare and decisive.

Sort the rest by EPSS. EPSS estimates the probability a CVE will be exploited in the next 30 days. FIRST measured that prioritizing everything at EPSS 0.10 or higher reaches 65.2% efficiency, against 3.96% for a “fix all CVSS 7+” strategy, while touching a fraction of the list. FIRST calls the 0.10 cutoff arbitrary, so treat it as a dial, not a law.

Break ties with CVSS. CVSS severity is the last input, not the first. It is useful for ordering two findings that are otherwise equal, and misleading as a primary sort.

These four signals, plus the decision framework SSVC, answer different questions and fail in different ways. The table is the short version.

SignalQuestion it answersKeyed onFails whenUse it to
ReachabilityDoes our code reach the flaw?Call graph into the dependencyDynamic loading, reflection obscure the pathDiscard unreachable CVEs first
CISA KEVIs it exploited in the wild now?CVE IDAttack has no CVE; evidence lagsForce an immediate fix
EPSSHow likely is exploitation soon?CVE IDNew CVEs with no history score lowOrder the long tail
CVSSHow bad is the flaw in theory?CVE IDIgnores your context entirelyBreak ties only

Verdict: start with reachability to delete what your code never calls, let KEV override everything reachable, order the remainder by EPSS, and keep CVSS for ties. None of the four, in any order, can rank a threat that was never assigned a CVE.

What is contextual vulnerability prioritization?

#

Contextual vulnerability prioritization is ranking that uses the facts a severity score omits: whether the vulnerable code is reachable, whether the asset is exposed, and whether the flaw is being exploited. Reachability is the sharpest of these for open source dependencies, because most of a CVE's blast radius is a single function you probably never call.

Take the lodash advisories in our lockfile. CVE-2021-23337 is command injection through lodash's _.template function. CVE-2026-4800 is the 2026 follow-up: the fix for the first bug validated the variable option but not options.imports key names, and both flow into the same Function() sink. Both are reachable only if your application passes untrusted input to _.template.

Almost no application uses _.template with attacker-controlled input; the common calls are _.get, _.map, _.cloneDeep. For those projects, both lodash CVEs are unreachable and belong at the bottom of the queue, regardless of their 7.2 and 8.1 CVSS scores. Socket's Full Application Reachability builds a call graph from your code into each dependency and typically marks about 80% of CVE findings unreachable, more than 90% in some ecosystems. It runs with socket scan create --reach and shows the exact call path when a finding is reachable. The analysis over-approximates: when it cannot prove no path exists, it reports the CVE as reachable rather than hide it.

The queue only ranks what has a CVE

#

Every signal in this article keys on a CVE. That is also the ceiling. Reachability, KEV, EPSS, and CVSS all assume the dangerous thing is a disclosed flaw in legitimate code, reached through your transitive dependencies. A malicious package is not a flaw; it is working code doing what its author intended, and it rarely gets a CVE. During the axios compromise in March 2026, a release pulled in a malicious dependency that executed at install time and left no entry any CVE queue would rank. The lockfile looked clean afterward.

So prioritization earns its keep and then stops. A queue ordered by reachability, exploitation, and probability is far shorter and far closer to real risk than one ordered by CVSS. It is also blind to the fastest-moving supply chain attacks, which never pass through the CVE pipeline at all. The sharpest ranking in the world cannot sort a threat that was never assigned a number.