Which CVE Alerts Actually Matter in Your Repo
Most CVE alerts in your dependency scanner are noise. Learn how reachability and context decide which vulnerability alerts are real risk worth fixing now.
Your dependency scanner says you have 240 vulnerabilities. Most of them do not matter, and if you try to fix all 240 you will waste a week and still miss the two that could actually breach you. The number a scanner reports is not a risk assessment. It is a list of every known CVE in every package anywhere in your tree, severity-labeled by people who have never seen your code and do not know whether the vulnerable function is ever called. The skill that matters is triage: separating the handful of alerts that are real, reachable, and reachable-by-an-attacker from the overwhelming majority that are theoretical. Get that right and security debt becomes manageable. Get it wrong and you either drown or ignore the whole feed.
Why raw CVE counts are noise
A CVE is a publicly disclosed vulnerability in some piece of software. Scanners match your dependency versions against the CVE database and flag every hit. That is useful raw material and a terrible priority list, for a few reasons.
Most flagged vulnerabilities are not reachable in your usage. A CVE in a function you never call is not exploitable in your application, full stop. The vulnerable code ships in your build but no execution path reaches it. Scanners cannot usually tell, so they flag it anyway.
Severity scores are context-free. A "critical" CVE is scored against a worst-case deployment. In your context, the same vulnerability might require local access you never grant, or affect a feature you disabled, or sit in a build-time tool that never touches production or user data. The label says critical. Your actual exposure says shrug.
And the sheer volume trains bad habits. When the feed cries wolf 240 times, people stop reading it. Alert fatigue is how the one real vulnerability gets ignored, which is the failure I warned about in transitive dependencies are the real risk.
The questions that separate real from noise
For each alert, I ask a short chain of questions, and most alerts die on the first one.
Is the vulnerable code reachable? Does any execution path in my application actually call the affected function? If not, it is not exploitable here. Reachability analysis is the single highest-leverage filter, and it kills the majority of alerts.
Is it reachable by an attacker? Reachable in code is not the same as reachable by a hostile input. A vulnerability behind authentication, on an internal-only service, or requiring a privilege the attacker would already need to have is far less urgent than one on an unauthenticated, internet-facing path.
What is the blast radius? If exploited, what does the attacker get? Access to customer data and secrets is a fire. A denial-of-service on a non-critical background job is a ticket.
Is there an exploit in the wild? A vulnerability with public, weaponized exploit code and active scanning has a shot clock. A theoretical one disclosed with no proof of concept does not. This is the difference between security debt's external clock and technical debt's internal one, which I unpack in security debt vs technical debt.
Run those four filters and 240 alerts collapse to a ranked shortlist, usually a very short one.
How reachability changes the math
Reachability is the concept that turns a scanner feed into a plan. Instead of "this package has a CVE," reachability answers "does my code path actually touch the vulnerable function." A tool like ReformCode ties CVE data to the actual call graph of your repo, so an alert on an unreachable function drops in priority automatically and an alert on a function sitting in your request path rises to the top. That is the difference between a list and a ranking, and the ranking is the entire value, the same principle I apply to all repo findings.
The practical effect: you fix the two reachable, attacker-facing, high-blast-radius vulnerabilities today, you schedule the reachable-but-lower-risk ones this cycle, and you consciously accept or defer the unreachable majority with a note explaining why. That last part matters. Deferring is a decision you record, not a thing you forget.
Do not swing to the other extreme
Triage is not permission to ignore the feed. Reachability analysis can be wrong, especially with dynamic dispatch, reflection, and plugin systems that call code no static analyzer sees. So treat "unreachable" as lower priority, not as zero, and re-evaluate when your code changes, because a refactor can make a previously unreachable vulnerability suddenly live. Keep the deferred list; review it when the dependency or the calling code moves.
The goal is a security backlog you actually work, ranked by real exploitability, with a standing budget and deadlines tied to how fast exploits are appearing, the way I laid out for prioritizing debt in how to prioritize which tech debt to fix first. A scanner that reports 240 vulnerabilities has told you nothing about what to do Monday morning. Reachability, attacker access, blast radius, and exploit availability tell you exactly what to do, in order, and let you ignore the rest with a clear conscience.