← Blog

Managing Open Source Vulnerabilities in Your Dependencies

A practical guide to open source vulnerability management: find vulnerable dependencies, prioritize with CVSS, EPSS, KEV and reachability, and remediate CVEs.

Bruno Baldo·Sep 28, 2026·Updated Sep 14, 2026·11 min read·Reviewed by Rainforest Technologies

Open source is the substrate of modern software. The libraries you install, and the libraries those libraries depend on, make up most of what actually ships to production — which means the security of your application is largely the security of code you did not write. Open source vulnerability management is the discipline of finding, prioritizing, and remediating the vulnerable dependencies in that supply of third-party code before an attacker does. It is a core practice within software composition analysis, and getting it right is the difference between a scanner that generates noise and a program that measurably reduces risk.

This article is a deep dive on the vulnerability side of SCA: where dependency vulnerabilities come from, how to tell a genuinely urgent one from a merely severe one, and how CVE remediation actually works when the flaw is buried three levels deep in your dependency tree.

Where dependency vulnerabilities come from

Every dependency you add is code running with your application's privileges, and it comes with its own history of discovered flaws. Those vulnerabilities arrive through two doors.

Direct dependencies are the packages you deliberately installed — the ones listed in your package.json, pom.xml, requirements.txt, or go.mod. You chose them, you can see them, and when one has a vulnerability the fix is usually within your control.

Transitive dependencies are the ones your direct dependencies pull in, and the ones those pull in, recursively. A single package you install can quietly bring dozens of others along with it. This is where most of the risk actually lives: studies of real-world projects consistently find that transitive dependencies vastly outnumber direct ones, and they are exactly the code most teams never look at. A vulnerability in a package you have never heard of, imported by a package you have never heard of, is still running in your application. Because this indirect exposure is so easy to miss, it deserves its own treatment — see transitive dependencies and security for the full picture.

The practical consequence is that you cannot manage what you cannot enumerate. The first job of open source vulnerability management is producing a complete, resolved inventory of every dependency in the tree, direct and transitive, at the exact versions that will actually run.

The vulnerability databases and identifiers

A dependency inventory only becomes useful when you match it against known vulnerabilities, and those live in public databases with their own identifiers.

  • CVE (Common Vulnerabilities and Exposures) is the universal naming scheme. A CVE ID like CVE-2021-44228 is a stable, globally unique label for a specific flaw, and it is the identifier everything else refers back to.
  • NVD (National Vulnerability Database) enriches CVEs with severity scores, affected-version ranges, and references. It is authoritative but not always fast; there can be a lag between a CVE being published and NVD fully analyzing it.
  • GHSA (GitHub Advisory Database) publishes advisories keyed to specific package ecosystems (npm, PyPI, Maven, and so on). Because advisories are expressed in terms of actual package names and version ranges, they map cleanly onto your dependency tree, and they often appear before NVD catches up.
  • OSV (Open Source Vulnerabilities) is a schema and aggregated database designed specifically for open source. It normalizes advisories from many ecosystems into a machine-readable, version-precise format, which makes automated matching far more reliable than parsing free-text version ranges.

Good tooling draws on several of these at once, because no single source is complete or timely for every ecosystem. The identifier that ties them together is the CVE, but the ecosystem-specific advisories (GHSA, OSV) are usually what let a scanner say with precision that your installed version is affected.

Scoring and prioritization: severity is not urgency

If you treat every disclosed CVE as an emergency, you will exhaust your team long before you run out of advisories. The skill in open source vulnerability management is prioritization — separating the handful of vulnerabilities that genuinely threaten you from the many that do not. Four signals, used together, make that possible.

CVSS (Common Vulnerability Scoring System) gives a 0–10 severity score. The number you usually see is the base score, which describes the intrinsic characteristics of the flaw in the abstract. But CVSS also defines an environmental score that adjusts for your context — whether the affected component is exposed, what it would actually compromise in your system. A base 9.8 on a library that only runs in a sandboxed dev tool is not the same risk as a base 7.5 on your internet-facing auth service. Base severity is a starting point, not a verdict.

EPSS (Exploit Prediction Scoring System) answers a different question: how likely is this vulnerability to be exploited in the wild in the near term? It expresses that as a probability. A high-CVSS flaw with a very low EPSS score is often a lower priority than a moderate-CVSS flaw that attackers are actively probing.

KEV (CISA's Known Exploited Vulnerabilities catalog) is the strongest signal of all: it lists vulnerabilities that are confirmed to be exploited in the real world. If a CVE in your tree is on the KEV list, it moves to the front of the queue regardless of its other scores. This is no longer theoretical risk.

Reachability cuts through the rest of the noise. A vulnerability only matters if the vulnerable code path is actually invoked by your application. Reachability analysis traces whether your code — or your dependencies' code — ever calls the affected function. If the vulnerable function is never reached, the CVE may be a genuine false-urgency: present in the inventory, but not exploitable in your usage. Reachability is one of the most effective ways to shrink a triage backlog from hundreds of findings to the few dozen that are truly live.

Put together, the prioritization question is not "how severe is this?" but "is it severe, likely to be exploited, known to be exploited, and reachable in our application?" That composite view turns a scanner's raw output into a ranked, actionable worklist.

Remediating direct vs. transitive dependencies

Once you know what to fix, the remediation path depends heavily on whether the vulnerable package is a direct or transitive dependency.

For direct dependencies, the options are relatively clean:

  • Upgrade to a patched version. This is the default and usually the right answer — most CVEs are fixed in a later release.
  • Patch the specific vulnerability where a maintainer or your tooling provides a targeted fix without a full version bump.
  • Replace the package with a maintained alternative if the project is abandoned and will never be fixed.
  • Accept the risk with a documented justification when the vulnerability is genuinely not reachable or not applicable, recording why so the decision is auditable and does not resurface as noise on every scan.

For transitive dependencies, you have a harder problem: you cannot simply upgrade a package you never imported. The approaches are:

  • Upgrade the parent that pulls in the vulnerable transitive dependency, so it resolves to a fixed version naturally. This is the cleanest fix when a newer parent exists.
  • Force a safe version using your package manager's override mechanism — overrides in npm, resolutions in Yarn, dependencyManagement in Maven, constraints files in pip. These tell the resolver to use a patched version of the transitive package even though nothing declared it directly. Use them carefully: you are overriding what a maintainer expected.
  • Replace or accept, as with direct dependencies, when no upgrade path exists.

In both cases, the fix has to be verified: bumping a version can resolve one CVE and introduce another, so remediation is followed by a re-scan, not assumed complete.

The upgrade problem

If upgrading always worked cleanly, dependency management would be a solved problem. It doesn't, and that friction is why vulnerable dependencies linger.

The core tension is staying current versus staying stable. A major-version upgrade to fix a CVE can bring breaking API changes that force refactoring you did not budget for. Teams that fall behind — pinned on an old major version because upgrading is painful — often find the patched version is several breaking releases away, turning a security fix into a migration project.

Lockfiles (package-lock.json, yarn.lock, poetry.lock, go.sum) are essential here. They pin the exact resolved version of every dependency, direct and transitive, so builds are reproducible and a scan result actually corresponds to what ships. But a lockfile also freezes vulnerable versions in place until you deliberately update it, which is why regenerating and reviewing lockfiles is part of routine maintenance rather than a one-time step.

The durable answer is to make upgrades small and frequent rather than large and rare. Staying reasonably current means each security upgrade is a minor bump instead of a multi-version leap, and it keeps you close enough to the maintained releases that a fix is usually one upgrade away.

Responding to a zero-day disclosure

Every so often a vulnerability lands that turns dependency management from routine hygiene into an all-hands scramble — a widely used library, a critical remotely exploitable flaw, active exploitation within hours. The Log4Shell disclosure in the Log4j library is the reference example: countless applications were affected through deeply transitive usage, and the hardest question for most organizations was simply are we even affected, and where?

This is where the groundwork pays off. A current software bill of materials (SBOM) turns that panicked, days-long manual audit into a query: search your SBOMs for the affected package and version range, and you have your exposed applications in minutes. SCA tooling that continuously matches your inventory against new advisories can flag the newly disclosed CVE across your whole estate the moment it is published.

It is worth being honest about the limits. An SBOM and SCA dramatically accelerate impact analysis; they do not make you immune. You still have to upgrade, override, or mitigate, and if no patch exists yet you may need a temporary workaround. What good tooling buys you is speed and certainty about scope — which, in the first hours of a zero-day, is often the difference between a controlled response and chaos.

Continuous monitoring

The single most important shift in mindset is this: a dependency that is safe today can be vulnerable tomorrow. You did not change anything — the world did. A new CVE gets disclosed against a version you shipped months ago, and suddenly a clean application has a critical finding.

That reality makes point-in-time scanning insufficient. Open source vulnerability management has to be continuous: your deployed artifacts and their SBOMs are re-evaluated against the databases as new advisories arrive, and someone is notified when a previously clean dependency becomes vulnerable. Monitoring the software you already run is just as important as scanning the code you are about to ship.

CI/CD gating, SLAs, and noise control

To operationalize all of this, most mature programs converge on a few practices.

Gate in the pipeline. Wire SCA into CI so a pull request that introduces a vulnerable dependency gets flagged on the change that caused it, when the fix is cheapest. Fail builds on new findings above an agreed threshold rather than on the entire backlog, so the gate blocks new risk without holding every build hostage to historical debt.

Set SLAs by severity. Define, and agree with engineering, how fast each tier must be remediated — for example, critical and KEV-listed findings within days, high within weeks, and lower severities on a routine cadence. Tying the clock to prioritized severity (not raw CVSS) keeps the commitments realistic and focused on real risk.

Control the noise, deliberately. The fastest way to kill an SCA program is to flood developers with findings they cannot act on. Use reachability to suppress unreachable CVEs, deduplicate the same vulnerability appearing across many services, and record accepted-risk decisions so they do not reappear on every scan. The goal is a short, trustworthy list — every item on it real, ranked, and owned.

None of these dependency risks exist in isolation from the rest of your application security. Vulnerable and outdated components remain one of the OWASP Top 10 (2025) risk categories, and software supply chain failures are tracked as category A03 — a direct acknowledgment that the code you pull in is now one of the primary ways applications get compromised. Open source vulnerability management is how you address that category in practice. See OWASP for how it fits the broader risk landscape.

How Rainforest helps

Doing all of this by hand — resolving the full dependency tree, matching it against several vulnerability databases, scoring findings across CVSS, EPSS, KEV, and reachability, and tracking remediation over time — is exactly the work SCA tooling exists to automate. Rainforest brings software composition analysis into your pipeline so you can inventory every direct and transitive dependency, surface the CVEs that actually affect the versions you run, and prioritize them by exploit likelihood and reachability rather than raw severity. Because Rainforest generates and continuously monitors an SBOM for your applications, a newly disclosed zero-day becomes a search across your estate instead of a fire drill, and remediation guidance points you at the upgrade or override that actually clears the finding.

If you want to bring open source vulnerability management under control across your dependency tree, book a demo and we will walk through it with your own applications.

Frequently asked questions

What is open source vulnerability management?

Open source vulnerability management is the practice of finding, prioritizing, and remediating known vulnerabilities in the third-party open-source dependencies your applications use — both the packages you install directly and the transitive ones those packages pull in. It involves building a complete dependency inventory, matching it against vulnerability databases like NVD, GitHub Advisories, and OSV, ranking the findings by real-world risk, remediating them through upgrades or overrides, and continuously monitoring deployed software as new vulnerabilities are disclosed. It is a core part of software composition analysis.

What is the difference between CVSS and EPSS?

CVSS (Common Vulnerability Scoring System) measures how severe a vulnerability is — its intrinsic technical impact, on a 0–10 scale. EPSS (Exploit Prediction Scoring System) measures how likely a vulnerability is to be exploited in the wild in the near term, expressed as a probability. They answer different questions: a flaw can be highly severe (high CVSS) yet unlikely to be exploited (low EPSS), or moderately severe yet actively targeted. Used together — ideally alongside CISA's KEV catalog and reachability analysis — they give a far better sense of true urgency than severity alone.

How do I fix a vulnerability in a transitive dependency?

Because you never imported a transitive dependency directly, you usually cannot just upgrade it. The cleanest fix is to upgrade the direct (parent) dependency that pulls it in, so it resolves to a patched version. If no suitable parent version exists, use your package manager's override mechanism — overrides in npm, resolutions in Yarn, dependencyManagement in Maven, or a constraints file in pip — to force the resolver onto a safe version. If neither works, you can replace the offending package or, when the flaw is genuinely unreachable, accept the risk with a documented justification. Always re-scan afterward to confirm the fix.

What is reachability analysis?

Reachability analysis determines whether the vulnerable code path in a dependency is actually invoked by your application. A package can contain a serious CVE that never matters to you because your code — and the rest of the dependency tree — never calls the affected function. By tracing which vulnerable functions are genuinely reachable, this analysis filters out findings that are present in your inventory but not exploitable in your usage, which dramatically reduces false urgency and lets teams focus remediation effort on the vulnerabilities that are truly live.

How often should I scan dependencies?

Continuously. Scanning should happen in CI on every change so new vulnerable dependencies are caught on the pull request that introduces them, and it should also run continuously against your deployed artifacts and their SBOMs. The reason is that a dependency safe today can be vulnerable tomorrow: new CVEs are disclosed against versions you already shipped without you changing anything. A one-time scan captures a single moment; only ongoing monitoring catches vulnerabilities that emerge in code already running in production.

Bruno Baldo

Written by

Bruno Baldo

CMO

Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Keep reading