The Risks of Transitive Dependencies in Supply Chain Security
Transitive dependencies are libraries your chosen tools rely on to function—components you didn’t pick but that you still end up needing for your project.
But while transitive dependencies are important, they carry hidden risks. Developers rarely know how many transitive dependencies exist in their projects, let alone whether they’re up-to-date or secure.
This lack of visibility makes them a common weak spot in the software supply chain, one that attackers have learned to exploit. In fact, most open-source vulnerabilities—about 95%—come from transitive or indirect dependencies.
We’ll break down why transitive dependencies matter, the risks they pose, and how you can deal with them to secure your software’s supply chain.
Direct Dependencies vs. Transitive Dependencies: What’s The Difference And Why Does It Matter?
Imagine you’re overseeing a home reno project. You hire vetted contractors, aka direct dependencies. But these contractors bring their teams—specialists who handle wiring, piping, and other tasks, aka transitive dependencies.
Now, if a subcontractor uses faulty wiring, the entire project is at risk—even though you played no part in hiring them. You could always ask your contractors to do all the work, but it doesn’t work like that in software development.
You can’t avoid transitive dependencies, which means most projects are built on open-source components, meaning developers choose a mix of libraries so transitive dependencies always come along for the ride.
What Are Direct Dependencies?
Direct dependencies are libraries you intentionally and explicitly add to a project. Think tools you choose to solve a specific problem or implement a feature.
For instance, if you’re building a web application and need a library to manage HTTP requests, you might add Axios.
What Are Transitive Dependencies?
Transitive dependencies are the layers below the surface—code you didn’t explicitly choose but which becomes part of your project.
Think of it this way: when you install Axios, it might rely on another library for parsing JSON or managing network protocols. Those secondary libraries, and any dependencies they require, become part of your codebase as transitive dependencies. You didn’t pick them, but you own their risks.
Aspect |
Direct Dependencies |
Transitive Dependencies |
Visibility |
Explicitly added and visible in configuration files. |
Hidden within the dependency tree. |
Control |
Fully managed by the developer. |
Indirectly included, updated independently. |
Management |
Developers are aware of updates and versions. |
Updates happen automatically via direct dependencies. |
Risk |
Easier to track and secure. |
Introduces unseen vulnerabilities into the codebase. |
Why Transitive Dependencies Are Risky
Transitive dependencies introduce vulnerabilities you can’t see, complicate updates, and expand your attack surface in ways that are easy to overlook. Here’s a quick breakdown of why this happens:
1. They Operate Out of Sight
Transitive dependencies don’t show up in your package manifest or configuration files. As a result, developers may have no idea they exist.
Without tools that map the entire dependency tree, these risks can remain invisible, lurking deep within your software. When vulnerabilities in these dependencies are exploited, they often catch teams off guard, leaving little time to respond.
2. They Multiply Vulnerabilities
Attackers don’t care whether a vulnerability is in your direct or transitive dependencies—they care about whether they can exploit it.
Transitive dependencies introduce code you didn’t vet, creating opportunities for attackers to slip in unnoticed. A single vulnerable library can act as a gateway, allowing attackers to execute code, exfiltrate data, or compromise entire systems.
3. They’re Harder to Manage
Managing direct dependencies is simple: you add the library, track updates, and keep everything compatible. Transitive dependencies are a different story. They update on their own, often without warning.
A routine bug fix to a direct dependency can pull in a new transitive dependency version—one that introduces vulnerabilities or breaks your code.
Things get trickier when multiple versions of the same transitive dependency creep into your project. When two direct dependencies need different versions of the same library, you’re stuck managing both. If you try to update one manually as a quick-fix solution, you can disrupt the entire dependency graph.
How Attackers Exploit Transitive Dependencies
Since transitive dependencies expand the attack surface, attackers can exploit them in many ways. These three types of common attacks show you what can happen:
Taking Advantage of Dependency Confusion
Imagine this: A dependency manager grabs the wrong library—not accidentally, but because it’s been tricked. This is known as dependency confusion.
And it’s exactly how Alex Birsan, a security researcher, managed to hack into companies like Apple and Microsoft in 2020.
Birsan’s experiment started off with a simple observation: Companies often use internal package names that inadvertently appear in public repositories. He used this knowledge to his advantage. He uploaded fake packages with those same names and assigned them higher version numbers. Because of that one simple change, those systems pulled his malicious packages instead.
The fallout was massive. His server received callbacks from dozens of major organizations These companies downloaded his code and executed it. Fortunately, Birsan’s intent was only to expose vulnerabilities. Had the attack been malicious, the impact could have been devastating—think data breaches or major financial and reputational losses.
Injecting Malicious Packages Sneakily
Sometimes, attackers don’t even need to create new packages. They simply prey on the ones developers already trust. They can sneak in malicious code injections without raising suspicion by taking over a maintainer’s account or adding harmful updates to legitimate libraries.
And not too long ago, attackers proved how easy this is to carry out. They used GitHub’s Dependabot—a tool developers rely on to manage updates, as their disguise. After gaining unsanctioned access, they submitted fake pull requests labeled as Dependabot contributions. These updates looked pretty routine to the untrained eye—titled “security fix” or “patch”—and appeared to come from GitHub’s automated system.
Naturally, developers trusted these updates and didn’t think too much before merging them. But hidden in these pull requests was code constructed to harvest and misuse sensitive data. This breach was an acute reminder that automating systems may save time and effort but automation isn’t an invitation to bypass reviews and checks, especially when the stakes are so high.
Exploiting Neglected Dependencies
Some libraries have a short shelf-life. Once important, these libraries are no longer maintained. And that’s why attackers love them: these rarely monitored libraries make for a perfect entry point.
The Log4Shell vulnerability in Apache Log4j, discovered in late 2021, is a prime example of this risk. Log4j had a flaw that allowed attackers to execute code remotely. But the real issue wasn’t far bigger than that. The actual vulnerability aside, it was how far deep Log4j was embedded. In many cases, developers didn’t even realize it was part of their systems because it was included as a transitive dependency.
Attackers acted fast. Systems across industries were compromised. Organizations faced an overwhelming task: locating and updating a single library hidden in layers of dependencies. Critical systems were left exposed while developers tried to play dependency whack-a-mole.
How to Handle Transitive Dependencies
Your codebase is only as strong as its weakest dependency. And transitive dependencies? They’re often the ones waiting to snap. Managing dependencies doesn’t have to feel like herding cats—here’s how to make it happen.
Start With an SBOM, Keep It Updated
First things first: know what you’re dealing with. Build a Software Bill of Materials (SBOM) that tracks every library, every dependency, and yes, every transitive dependency in your code.
And don’t let it collect dust. Treat it like a live document—if it’s outdated, it’s useless.
Dig Deep With Dependency Audits
Here’s the bottom line: not all vulnerabilities are obvious. Transitive dependencies hide out in the back corners of your projects, which means shallow scans won’t cut it.
Use tools designed to peel back the layers and look for outdated libraries, security flaws, and anything else that could sink your application. Don’t think of audits as a one-and-done activity. Make them routine, like brushing your teeth—but for your code.
Lock It Down Before It Spirals
Letting transitive dependencies update whenever they want? That’s playing with fire. One moment, everything works fine. The next, you’re stuck untangling a mess of broken builds or unexpected vulnerabilities.
The fix? Take control. Use fixed versions for your direct dependencies. This won’t stop updates entirely, but it’ll give you the final say on when and how changes flow into your projects.
Keep Your Code—and Sanity—Safe
Transitive dependencies are sneaky, but they don’t have to be dangerous. With a solid SBOM, regular audits, and strict version controls, you can be proactive.
It’s not glamorous, but its' better than dealing with the aftermath of a preventable security breach.
Reframe Dependency Management as a Shared Responsibility
Dependency management is a cultural problem—not just a tooling one.
Developers need to understand the risks associated with transitive dependencies and proactively review their projects.
Build a Secure Software Supply Chain With Codacy
Transitive dependencies may be hidden, but their risks are anything but. And they don’t stop at hurting your code—they can ripple out to compromise your users, your reputation, and your bottom line.
Visibility is your first layer of defense: know what’s in your dependency tree and understand how vulnerabilities travel through transitive dependencies. Next comes action—identify, prioritize, and address risks before they snowball into larger problems.
That’s where Codacy comes in. With features like Static Application Security Testing (SAST), Software Composition Analysis (SCA), secrets detection, and our Dependency Explorer, Codacy helps you uncover vulnerabilities across your codebase—both direct and transitive.
Want to move away from reactive fixes? Sign up for a free Codacy trial today and protect your software at every layer.