12 November 2026

Dependency Vulnerabilities: Which Ones Actually Reach Your Code

Dependency Vulnerabilities: Which Ones Actually Reach Your Code
4 min. readBy Borg

Switch on a dependency scanner and within minutes you have a list: every package version that matches a published advisory, each with a severity. On a codebase of any age that list runs to hundreds of entries.

The list is accurate about what it claims. Those versions are in the lockfile, and those advisories apply to those versions. What it leaves out is the one thing a developer needs before spending an afternoon on an upgrade: can our application actually be attacked through this?The proportion of advisories that actually reach your code is worth measuring, and this is how.Where the time goesFor one advisory, a careful developer reads it and identifies the vulnerable function. They find out how the package got into the project, check whether it ships to production, search for where it is called, and work out whether anything an attacker controls gets there. Then they write the conclusion down somewhere it will be found next quarter.Some of those steps take a minute. The last two can take an hour and need someone who knows the codebase. At fifteen minutes an advisory, two hundred of them is fifty hours. Few teams have that, so the list is sorted by severity and worked until patience runs out, or left until an audit. Neither is a decision about risk.What a match tells youA scanner compares versions against advisory databases. A match means this version is inside the affected range. That is a statement about the package, not about your application. A critical advisory is critical for some application that uses the library in the affected way. Whether yours is that application is a separate question.Exploitation data helps with order. EPSS and the CISA Known Exploited Vulnerabilities catalogue say how likely a CVE is to be exploited somewhere, and whether it already has been. As we covered in how to prioritise security findings that have no CVE, they are good reasons to look sooner. They still cannot tell you whether the vulnerable code runs in your application.Rule out in orderMost of the work is ruling advisories out, and the questions come in a useful order, cheapest first.

  • Is this version really in the affected range?
  • Does the package ship, or is it only used by tests and build tooling?
  • Is it loaded by anything that runs?
  • Does the vulnerable function run?
  • Can outside input reach it?

The first three can be answered from the dependency and import graphs, without reading application logic. The fourth needs the advisory to be understood rather than matched. The fifth depends on what the application does with its data, and it is where the answer is most often provisional.Each "no" is a reason, and a reason can be recorded once instead of rediscovered every quarter.What is leftAn advisory that survives is not automatically exploitable. It is not ruled out, which is weaker. Calling it exploitable should mean a path has been traced, from input an attacker controls to the vulnerable call. Stronger still is trying it: a harmless request against the running application that reaches the vulnerable behaviour settles what a trace can only argue.So there are three honest outcomes. Ruled out, with the reason. Exploitable, with the evidence. And undecided, which is work for a person.Dependencies in Odin runs these questions on every advisory. Each package gets a verdict with its reason in a sentence. Severity orders packages within a verdict rather than deciding it, and a package opens to show how your code reaches it. Where you have turned on the check against the running app, Odin confirms the path there. What it cannot decide is marked Needs review.What developers get backThe unit of work changes. Instead of a list to investigate, a developer gets a few packages with a reason attached. Reading a one-line reason and disagreeing with it takes minutes. Producing it from scratch is the hour.The conclusion also persists, so the next person or the next audit does not repeat it. And upgrades stop being all or nothing: fix the few that matter this week, and take the rest on the normal cadence.The upgrade itself does not go away. An exploitable package still has to be patched, tested and shipped. What goes away is reading advisories for code that never runs.Where this stopsA ruling holds for the code as it was scanned. Add a feature that calls the vulnerable function and the answer changes, so the check has to run again when the code changes and when new advisories arrive.It is also not a reason to stop upgrading. Unreachable today is the cheapest kind of vulnerability to remove. And some contracts and compliance regimes require patching by severity and date, whatever the reachability.

Borg on
Social Media

Borg specializes in securing complex, high-risk digital systems.