Your app has hundreds of dependencies because every package you add brings the packages it needs, and those bring more. You chose a handful. Your tools installed the rest. This hidden tree matters for security because a flaw in any branch can end up inside your app.
TL;DR
- You list a few packages yourself. Those are direct dependencies.
- Each of them depends on others. Those are transitive dependencies, and they make up most of the tree.
- Security scanners must read the whole tree, not just your short list.
- A bigger tree is not automatically unsafe, but it is more to watch.
- You can shrink the tree by removing what you do not use.
What is a dependency, and what is a transitive one?
Definition: A dependency is a piece of code written by someone else that your software needs in order to run. A transitive dependency is a dependency of a dependency, which you did not choose yourself.
Imagine you are baking a cake and you buy a ready made cake mix. The mix contains flour, sugar, and a leavening agent. You did not buy those separately, yet they are in your kitchen now. If the maker of the sugar recalls a batch, your cake is affected even though sugar was never on your shopping list.
Software works the same way. You add a package to do something useful, such as formatting dates or sending an email. That package was built on other small packages. Programmers like to reuse small, focused pieces instead of rewriting them, so the chain of reuse can run several levels deep.
For a longer explanation of the terms, see what is a dependency in programming and direct versus transitive dependencies.
How does a short list turn into hundreds?
The growth is like a family tree. One person has two children, each of whom has two children, and a few generations later the number of relatives is large. Packages behave similarly, except that branches often share the same relatives.
In a project that uses npm, the file that holds your own list is called the manifest. The npm documentation describes the dependencies section of that file as an object that maps a package name to a version range. That is the list you wrote. A second file, the lockfile, is generated by the package manager. The npm docs say it describes the exact tree that was generated, so that later installs produce identical trees. That second file is where the real size shows up.
Here is a simple table of how the layers work.
| Layer | Who chose it | Typical size | Security question |
|---|---|---|---|
| Your own code | You | Small to large | Is my logic safe? |
| Direct dependencies | You | A handful to a few dozen | Do I trust these packages? |
| Transitive dependencies | Your direct packages | Often many times larger than the direct list | Do I even know what these are? |
| Development tools | You or your team | Varies | Do they ship to customers, or only run on my laptop? |
Development tools deserve a note. Packages that only help you build or test, such as test runners, sit in a separate list. The npm docs say test harnesses and transpilers belong under a separate development section, not in the main dependencies. That is useful for scanning, because a flaw in a test tool often matters less than a flaw in code that runs in production.
A worked example with invented numbers
These numbers are invented to show how the tree can grow. They are not measurements of a real project.
Suppose your app lists 12 direct packages.
- Each direct package depends on 4 others on average. That adds 48 second level packages.
- Each of those depends on 3 others on average. That adds 144 third level packages.
- Many of these are the same package reached by different routes. Say about 40 percent are repeats.
Count it up. Without repeats, the tree would have 12 plus 48 plus 144, which is 204 entries. If 40 percent of the lower layers are repeats, you remove about 77 duplicates from the 192 lower entries, leaving about 115 unique lower packages. Add your 12 and you have roughly 127 unique packages.
So a list you could read in a minute became more than 120 pieces of someone else's code. If just 1 percent of packages have a known flaw at any moment, you would expect one or two findings in a project this size. You did not make a mistake. You just inherited a tree.
The lesson is not that the tree is too big. The lesson is that you cannot manage a tree you cannot see.
Why does the size matter for security?
Because a flaw anywhere in the tree runs with the same permissions as your app. The OWASP Top 10 makes this point in its discussion of vulnerable and outdated components. It says components typically run with the same privileges as the application itself, so flaws in any component can result in serious impact. It also lists, among the signs you are likely vulnerable, not knowing the versions of all components you use, both client side and server side, including nested dependencies.
Three things make a big tree harder to protect.
- Less attention per package. You may never have read the code of a package five levels down.
- More update work. Each package gets new versions on its own schedule, and an update to one can require updates to others.
- Hidden ownership. When a deep package has a flaw, the fix often has to come from the package that pulled it in, not from you. See direct versus transitive vulnerabilities for how that changes your plan.
A larger tree also gives attackers more places to try. Some attacks publish a look alike package with a name close to a real one, hoping someone installs it by mistake. The more names in your tree, the more names to double check.
How can I see my own tree?
You have three simple options, from easiest to most complete.
- Open the lockfile. It is a text file. You can search it for a package name. It is long, but it is the truth about what was installed.
- Ask your package manager. Most package managers have a command that prints the tree. For npm, the list command shows packages and what needs them. Use it to answer the question "why is this here?"
- Run an SCA tool. Software composition analysis reads the tree and gives you a searchable inventory with versions, licenses, and known flaws. The define SCA post covers what this kind of tool does.
When you look, ask these questions about the biggest branches:
- Do we still use this direct package at all?
- Is there a lighter way to do the same job?
- Are there two packages here that do the same thing?
- Does a development tool leak into the production list?
How do I shrink the tree safely?
You will not get to a tiny tree, and that is fine. Aim for a tree you understand.
- Remove direct packages you no longer use. The OWASP guidance names this first in its patch management advice: remove unused dependencies, unnecessary features, components, files, and documentation.
- Before you add a new package, check what it brings with it. A tiny helper that pulls in 30 packages may not be worth it.
- Keep development tools in the development list so scanners and builds can treat them separately.
- Update on a regular schedule so you do not fall many versions behind.
- Keep the lockfile in version control so every teammate and every build gets the same tree.
Try it yourself
The demo below shows an SCA dependency inventory for a sample workspace. Click through and look for how the list separates direct from transitive components, and how many entries sit below the ones a person chose.
Click through it at your own pace, or open it full screen.
Quick check
Is a project with more dependencies always less secure?
Not always. Risk depends on which packages are in the tree, how current they are, and whether the vulnerable code can run. But more packages mean more to monitor, so a smaller, well understood tree is easier to protect.
What is the difference between a direct and a transitive dependency?
A direct dependency is one you chose and listed yourself. A transitive dependency is one that your direct packages need, which you did not choose. Most of the tree is usually transitive.
Can I remove a transitive dependency myself?
Usually not directly. It is there because another package needs it. You can upgrade or replace the package that pulls it in, or drop that package if you no longer need it.
How can Safeguard help with this?
Safeguard's SBOM and dependency scanning builds the full component list for a project, direct and transitive, and checks it against known vulnerabilities. An SBOM, or software bill of materials, is a formal ingredient list for software. See what is an SBOM if the term is new. Reachability analysis then helps show which flagged components sit in code your app can actually call, so a large tree does not turn into a large to do list.
You can run a free scan at gold.safeguard.sh/scan to see your own inventory.
Sources
- OWASP Top 10 2021, A06 Vulnerable and Outdated Components (source text on GitHub)
- npm Docs: package.json
- npm Docs: package-lock.json
Last reviewed: 19 October 2026.