Patch and vulnerability management
A new scan drops 4,000 findings and you have time for forty. CVSS alone is a trap — it ranks severity in a vacuum. Real prioritization multiplies severity by exposure: is it reachable, exploited in the wild, and on a box that touches your data?
Monday morning, the scanner finishes its weekly sweep and hands you a CSV with 4,118 rows. Forty-one of them are CVSS 9.8 “Critical.” Your team has maybe forty engineer-hours of patching capacity this sprint, and three of those criticals are in a vendored XML parser you don’t even call. Meanwhile, buried at CVSS 6.5 “Medium,” sits a deserialization bug in the auth service — internet-facing, with a public exploit on GitHub that’s been used in the wild for a week. If you patch top-down by CVSS, you’ll burn the sprint on the parser nobody can reach and ship the auth-service breach into next month. The number on the row is not the risk. The risk is the number times where the box lives and whether anyone is actually pulling the trigger.
By the end of this lesson you’ll know why CVSS base score is the wrong sort order, and how to rank a flood of findings by severity times exposure so the forty hours you have land on the forty bugs that can actually hurt you.
Two pipelines, not one
People say “patching” and mean two different machines bolted together. Separating them is the first senior move, because they have different inputs, owners, and failure modes.
Vulnerability management is the discovery-and-decide loop: inventory every asset, scan continuously, correlate each finding against a vulnerability feed (NVD, vendor advisories, your SBOM), then decide — patch, mitigate, or accept. Patch management is the execution arm: acquire the fix, test it in staging, roll it to production in waves, and verify it actually landed. The first answers what is wrong and does it matter; the second answers how do we ship the fix without breaking prod.
The reason teams drown is they collapse the two. They let the scanner’s raw output be the work queue, so every Tuesday the queue is 4,000 items long and morale evaporates. The discipline is: vulnerability management triages the flood down to a ranked, finite list with deadlines, and only then does patch management pull from it. A scanner that dumps unranked findings into a ticket queue isn’t a program — it’s a denial-of-service attack on your own engineers.
Why CVSS base score is the wrong sort key
CVSS gives every vulnerability a 0–10 base score from intrinsic metrics: attack vector, complexity, privileges required, and the CIA impact. That base score is genuinely useful — it’s a standardized, vendor-neutral way to say “this flaw, in the abstract, is severe.” But the word that kills people is abstract. The base score deliberately knows nothing about your environment: not whether the vulnerable code path is reachable, not whether the box is internet-facing or buried three subnets deep, not whether anyone has ever written an exploit for it.
The empirical case is brutal. Studies of disclosed CVEs consistently find that only a small minority — on the order of 5% — are ever exploited in the wild, yet roughly 60% of all CVEs score CVSS 7.0 or higher (“High” or “Critical”). Sort by base score and “High/Critical” stops meaning anything: it’s most of the list. CVSS itself says the base score is not a risk score and must be combined with environmental and temporal context before you act on it — guidance that the average “patch all Criticals in 30 days” policy quietly ignores.
So the senior reframe is a multiplication, not a lookup. Risk ≈ severity × exposure, where exposure is the part CVSS leaves out:
- Exploitability in the wild — is there a public proof-of-concept? Is it in CISA’s Known Exploited Vulnerabilities (KEV) catalog, or scoring high on EPSS (the model that predicts the probability a CVE will be exploited in the next 30 days)? A KEV-listed bug is being used right now; that beats a theoretical 9.8 every time.
- Reachability — is the vulnerable function actually called by your code, and is the asset reachable from where the attacker stands? A critical in a dependency you import but never invoke is latent, not live.
- Blast radius — what does this box touch? An RCE on the box that holds the customer database is a different universe from the same RCE on an isolated batch worker.
| Finding | CVSS base | Exposure signals | Real priority |
|---|---|---|---|
| Deserialization RCE in auth service | 6.5 (Medium) | Internet-facing, in KEV, public PoC, touches user store | P0 — patch today |
| XML parser RCE in vendored lib | 9.8 (Critical) | Code path never invoked, not in KEV, EPSS ~1% | P3 — patch in routine cycle |
| Privilege-escalation in kernel | 7.8 (High) | Internal only, needs local access, no PoC | P2 — next patch wave |
| Info-leak in admin tool | 5.3 (Medium) | VPN-only, low-value data, no exploit | P4 — accept / backlog |
Read the first two rows together: the 6.5 outranks the 9.8. Any policy that sorts by the CVSS column alone ships exactly the wrong order.
▸Why this works
Why not just patch everything and skip the ranking? Because patch capacity is hard-capped and patching has a cost of its own. Every change is a regression risk — the patch that fixes the CVE can break a production code path, and the worst outages of the decade have come from bad patches, not bugs left unpatched. Testing, canarying, and the occasional rollback all consume the same finite engineer-hours. So “patch all 4,000” isn’t a safer choice than ranking; it’s an unfunded mandate that, in practice, means the queue never empties and the genuinely urgent items wait behind harmless ones. Ranking is how you spend a fixed budget where it buys the most risk reduction.
SLAs turn priority into a clock
A ranked list with no deadline rots. The mechanism that keeps it moving is the remediation SLA: a contract that says a finding of a given priority must be remediated within a fixed window, measured from discovery, and tracked. Typical shapes look like P0/Critical-exploited → 24–72 hours (or emergency out-of-band), High → 7–30 days, Medium → 30–90 days, Low → next routine cycle or risk-accept.
Two things make SLAs real rather than theater. First, the clock starts at discovery, not at “when we got around to it” — so a finding that sits untriaged is already burning its budget, which is the pressure you want. Second, every SLA needs a defined exception path: when you genuinely can’t patch in time (no vendor fix yet, patch breaks a critical workflow), you record a compensating control — a WAF rule, network isolation, a feature flag off — and an explicit, owned, time-boxed acceptance. An SLA with no exception path doesn’t get followed; it gets quietly ignored, and then you have a policy that lies. The “mean time to remediate” against these SLAs is the one metric that tells you whether the program is actually shrinking your exposure window or just generating tickets.
A weekly scan returns 41 CVSS-9.8 Criticals and, at 6.5 Medium, a deserialization RCE in your internet-facing auth service that's listed in CISA KEV with a public exploit. You have ~40 engineer-hours this sprint. Pick the prioritization.
Why is sorting a findings list by CVSS base score descending a poor prioritization strategy?
A genuinely Critical, reachable vulnerability has no vendor patch available yet, and your SLA window is about to expire. What's the correct move?
Order the vulnerability-to-remediation pipeline from first step to last:
- 1 Inventory assets and scan continuously for findings
- 2 Enrich each finding with exploit + exposure context (KEV, EPSS, reachability)
- 3 Prioritize by severity x exposure and assign an SLA deadline
- 4 Test the fix in staging, then roll to production in waves
- 5 Re-scan to verify the patch actually landed, then loop
- 01Why is CVSS base score the wrong key to sort a flood of findings by, and what should you multiply it by instead?
- 02What is a remediation SLA, why must the clock start at discovery, and what does its exception path do?
Patching is really two pipelines: vulnerability management discovers, enriches, and ranks findings down to a finite list; patch management tests, rolls, and verifies the fixes. Teams drown when they collapse the two and let the raw scan — 4,000 rows every Tuesday — be the work queue. The cardinal mistake is sorting by CVSS base score: it’s severity in a vacuum, ~60% of CVEs are High/Critical, and only ~5% are ever exploited, so the column barely sorts anything. Real priority is severity × exposure — is it exploited in the wild (CISA KEV, EPSS), is the code path reachable, and what’s the blast radius? — which is why a KEV-listed, internet-facing 6.5 in your auth service outranks a 9.8 in code you never call. Attach a remediation SLA whose clock starts at discovery and whose exception path lets you record a compensating control and an owned, time-boxed acceptance when no patch exists, then verify by re-scanning rather than assuming. Next time a scanner hands you 4,000 findings, your first question isn’t “what’s the highest CVSS” — it’s “which of these is reachable, exploited, and near my data.”
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.
Apply this
Put this lesson to work on a real build.