By clicking “Accept”, you agree to the storing of cookies on your device to enhance site navigation, analyze site usage, and assist in our marketing efforts. View our Privacy Policy for more information.
18px_cookie
e-remove

Demystifying Transitive Dependency Vulnerabilities

Written by
Darren Meyer
Darren Meyer
Published on
May 31, 2024

Modern applications, as you probably know, are less “write from scratch” and much more “adopt and coordinate open source components”. 80% or more of the code in a modern application is provided by those open source (OSS) dependencies. But not every dependency is one your developers chose explicitly; some are transitive dependencies.

Because transitive dependencies do have some unique considerations to dependency management — and therefore to the dependency security part of your Software Supply Chain Security program — discussions of the topic tend to introduce confusion. Let’s demystify transitive dependencies.

Quick Summary of Transitive Dependencies

  • Third-party libraries and frameworks your developers add to your applications are “direct dependencies”; those libraries and frameworks have their own direct dependencies, which are your “transitive dependencies”.
  • If there’s a risk in a dependency, regardless of whether it’s transitive or direct, it could potentially affect your application; reachability analysis is a powerful tool for determining whether dependency risks are relevant to your application.
  • Remediating risks in transitive dependencies often requires updating the right direct dependency, and your tools should point the way.

What Makes a Dependency “Transitive”?

The libraries and frameworks that your developers adopt are your direct dependencies. But maintainers of those libraries and frameworks use similar development tactics as your development teams: they too will select libraries to depend upon rather than writing everything from scratch. Those dependencies end up in your code too, even though your developers don’t explicitly choose them. We call these dependencies-of-your-dependencies “transitive dependencies.”

Imagine you buy ice cream with Oreo cookie crumbles in it (a personal favorite of mine!). Those cookie crumbles are a direct dependency for the ice cream maker. But they’re made with sugar and flour and other ingredients. Those are transitive dependencies for the ice cream maker. And this chain keeps going! The flour often has grain, preservatives, vitamins, etc. added. Those are dependencies of the flour and transitive dependencies for the Oreos and the ice cream maker. 

Transitive dependencies are like the ingredients for your ingredients

At one level, the distinction doesn’t matter: if you’re allergic to wheat, it doesn’t matter whether the ice cream maker added wheat or if the Oreos are made with wheat — the issue will affect you. For the ice cream maker, it matters more because they’re unlikely to find it useful to approach the flour maker for a quality issue. Instead, they’re going to respond to problems anywhere in the supply chain by dealing with their suppliers (their direct dependencies). 

It’s the same with software. If you have a vulnerability that impacts your software , it doesn’t matter if it’s in a direct or transitive dependency. But where it falls in that chain can make a big difference in how you discover, assess, and respond to the issue. 

Do I Care at All About Transitive Dependencies?

You should, for two simple reasons: your auditors care and risk is risk.

Your Auditors Care

If you need a software inventory or SBOM, you need it to be complete and accurate. Especially if you have a regulator or auditor requirement for managing vulnerabilities, producing SBOMs (e.g. EO 14028), or reporting material risks. And that means discovering and listing all the dependencies, regardless of whether they’re direct or transitive.

Certain regulatory, legal, or contract compliance considerations (like PCI DSS or FedRAMP), are concerned with all the vulnerabilities that might impact you, whether they’re in direct or transitive dependencies doesn’t really matter to them. But auditors do generally accept evidence that a vulnerability doesn’t need to be fixed if it poses no risk — to this end, being able to determine, with a systematic and evidence-based approach, that a given vulnerability is completely unreachable can be a huge relief to your compliance burden.

Risk Is Risk

As I said above:

If there’s risk, it doesn’t really matter where it is, it only matters if it affects you.

Or put another way, your adversaries don’t care if a vulnerability is in a transitive dependency or not, they only care if they can manage to exploit it. Besides, in a surprising number of cases, when you have a transitive dependency relationship, your first-party code is actually directly calling methods in those transitive dependencies. In other words, while the dependency relationship is transitive, you might still have direct relationships between your first party code and the code in those dependencies.

Why Does It Matter Whether a Dependency Is Transitive or Direct?

The relationship a dependency has to your first-party code matters because it changes how you assess and respond to risks. And since, statistically, most of the code that makes up your application — and therefore most of the vulnerabilities and other risks (95%, according to our research with Station 9) — are in these transitive dependencies, it’s important to understand the ways they’re a bit complex to deal with.

There are three things that make dealing with transitive dependencies a little weirder than dealing with direct dependencies:

  1. You can have multiple different versions of the same transitive dependency in your application
  2. It’s harder to see what parts of a transitive dependency are actually being used
  3. It’s generally a bad idea to update transitive dependencies on your own

You Can Have Multiple Versions in Your Application

If I have two different direct dependencies (let’s call them ‘foo’ and ‘bar’, as is tradition) that both depend on ‘baz’, then I might have two entirely different versions of baz in my project. Maybe foo@1.0 depends on baz@1.1.14 and bar@2.4 depends on baz@1.2.0 — I actually have both of those baz versions in my package. If there’s a vulnerability in baz@1.2.0, in order to know if that affects me, I’ll need to understand that the vulnerable function is only reachable if my path goes through bar, and is NOT reachable if it goes through foo.

Direct dependencies calling separate transitive dependency versions

Endor Labs handles this gracefully; we model dependencies as a graph, and so can show you why each version of a transitive dependency is required (that is, what direct dependency requested it). And because our reachability analysis statically models call paths through to these transitive dependencies, we can determine if there’s a likely path from your first party code to exactly which transitive dependency, which means we can tell you which direct dependency needs to be updated to fix the transitive vulnerability.

Call graph example

It’s Harder to See What’s Being Used

While it’s usually relatively easy to see what functions and methods and other symbols your first-party code uses in a direct dependency, it can be challenging to see what parts of a specific transitive dependency are being called where. You could be calling transitive dependency functions from first-party code, but it’s much more likely that you call something in a direct dependency which calls something in a transitive dependency, and on down the chain.

Being able to confidently assert that a vulnerability in a dependency is unreachable (and therefore poses no risk), regardless of whether that dependency is direct or transitive, allows you to speed up your remediation efforts. There’s no point spending valuable developer time on updating a library when there’s no risk to address. And auditors for PCI DSS and FedRAMP, among others, are increasingly accepting high-quality reachability analysis as sufficient evidence to consider a vulnerability a “false positive” as far as the compliance requirement is concerned, so this time savings can apply to you even in highly compliance-driven environments.

To address this, Endor Labs is constantly statically analyzing these call chains in the OSS library ecosystem, and we can “stitch” them together with our analysis of your first-party code. This lets us very quickly and very accurately determine whether a vulnerability in one of your transitive dependencies has a code path that could be exploited. In fact, our transitive path reachability analysis is just as accurate as it is for direct dependencies. We knew from day one that this was important, and it’s why we put nine PhDs on our staff to push the state of the art on this very problem.

Updating Transitive Dependencies is Weird

While most package managers do have a way for you to force an update of a transitive dependency on its own, it’s often a very bad idea. Not only are you taking a risk of breaking something else in your dependency graph — because updates are not risk-free — but you’re adding a new maintenance challenge. When you override the dependency choices your direct dependencies make, you also run the risk of future updates to the direct dependency not working with the transitive version you chose.

If that made your eyes glaze over, then you still get it — updating transitive dependencies yourself is not usually what you want. 

This is why Endor Labs findings do not advise developers to make updates to a transitive dependency. Because we’re aware of the relationship a transitive dependency has to its “parent” direct dependency, we use our enriched OSS library version database to advise developers on what direct dependency to update in order to resolve a vulnerability in a transitive dependency. To call back to our example where bar@2.4 depends on baz@1.2.0, we can advise developers to update to bar@2.6, which depends on baz@1.2.1, which has repaired the vulnerability.

Here’s a real-world example. The project langchain4j imports redis’ jedis library; the version they requested at the time of this scan imported a json library that has a high-severity DDOS vulnerability in place. That json library is a transitive dependency for lanchain4j, so our recommendation is to change the version of jedis to one that imports a safe version of the json library; we provide both downgrade and upgrade options so that developers can choose the fix path that best suits their needs. 

Transitive dependency fix options from Endor Labs

In short, we tell you the right thing to update, so you don’t get stuck in Transitive Dependency Hell.

Detect Transitive Dependency Vulnerabilities with Endor Labs

A software composition analysis (SCA) tool is the most common way to detect transitive dependency risk. Endor Labs Open Source includes reachability-based SCA:

  • Accurate inventory— ‍Look beyond manifest files to pinpoint all direct and transitive dependencies, including phantom (undeclared) dependencies.
  • Prioritize in seconds— Find reachable vulnerabilities at a function-level in both direct and transitive dependencies, all without any dreadful runtime agents.
  • Identify supply chain attacks— Look beyond vulnerabilities and licenses to discover the OSS Top 10 risks including malware, outdated, and unmaintained dependencies.

In addition to SCA, Endor Labs Open Source also includes artifact signing, AI-assisted OSS selection, SBOM & VEX generation, and container scanning. To compare results against your current tool, Endor Labs offers a free, full-featured 30-day trial that includes test projects and the ability to scan your own projects.

The Challenge

The Solution

The Impact

Get a Demo

Get a Demo

Get a Demo

Welcome to the resistance
Oops! Something went wrong while submitting the form.

Get a Demo

Get a Demo

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Get a Demo