Safeguard
Supply Chain•

Why do two SCA tools report different dependency counts?

Two SCA tools can scan one repo and list different numbers of dependencies. Scope, dev packages, lockfiles, and deduplication explain most gaps.

Safeguard Security Team
6 min read

Two SCA tools often report different dependency counts for the same repository because they count different things: direct versus transitive packages, development versus runtime scope, manifests versus resolved lockfiles, and unique packages versus unique package-and-version pairs. A different number is a prompt to compare definitions, not proof that one tool is wrong.

The five usual causes

  1. Scope. One tool includes dev and test dependencies and the other lists only what ships. The CycloneDX specification, for instance, lets a component carry a scope, so the same package can be required or excluded depending on policy.
  2. Resolution source. A scanner reading only the manifest sees version ranges. One reading a lockfile sees the exact resolved tree, which is usually larger.
  3. Depth. Some tools stop at direct dependencies unless asked. The CycloneDX dependency graph can represent both direct and transitive relationships, so a full inventory is possible when the tool builds it.
  4. Deduplication. Is one package at three versions counted once or three times? Is a package used in ten workspaces counted ten times?
  5. Ecosystem coverage. One tool may parse a build file the other skips, such as a secondary language in a polyglot repository.

How to compare them fairly

Export both results in a standard format if the tools allow it, and normalize them to a package URL plus version. Then compute three lists: found by both, found only by tool A, found only by tool B.

BucketWhat to do
Found by bothTreat as the agreed baseline
Only ACheck whether it is dev scope, a different depth, or an ecosystem B misses
Only BSame check in reverse

Sample ten items from each exclusive bucket and trace them to the file that declares them. You will usually find a pattern within a few minutes, such as "tool B skips the Gradle subproject".

Why the number is less important than coverage

A dependency count is not a quality score. A tool that reports a smaller count because it excludes test-only packages may be doing exactly what you configured. The question to ask is whether anything that ships to production is missing. The CycloneDX specification also has a compositions concept that records whether an inventory is complete, incomplete, or unknown. A tool that tells you where it is uncertain is more useful than one that reports a confident number.

A worked example

Take a service with a package manifest listing 40 direct dependencies and a lockfile that resolves to 900 packages in total, of which 300 are development tools such as test runners and linters. Run two tools.

Tool A reads only the manifest and reports 40 dependencies. Tool B reads the lockfile and reports 900. Tool C reads the lockfile but excludes development scope and reports 600. All three are describing the same repository, and each is correct for its own definition.

Now add duplication. If the 600 include 80 packages that appear at two versions each, a tool that counts unique package names says 520, and one that counts name and version pairs says 600. Add a monorepo with five services sharing most packages, and a per-service count can be five times a per-organization count.

The lesson is to compare definitions before comparing numbers.

Step by step: reconcile two reports

  1. Decide the definition you want. A common choice is runtime scope, resolved from lockfiles, counted as unique package URL plus version.
  2. Configure both tools to match that definition as far as they allow.
  3. Export each result to a standard format such as CycloneDX or SPDX, or to a simple list of identifiers.
  4. Normalize each entry to ecosystem, name, and version.
  5. Compute the three buckets: both, only one, only the other.
  6. Pick ten items from each exclusive bucket and trace each to the declaring file.
  7. Write a short finding for each pattern, such as "tool B ignores the second Gradle module".
  8. Report the pattern to the vendor and note the answer.

Patterns you are likely to find

  • A tool skips a subdirectory because of an ignore setting you did not know existed.
  • A tool treats a workspace as one project and another treats each package as its own.
  • A scanner resolves ranges differently without a lockfile, producing a different version for the same package.
  • A tool adds operating system packages from a container layer and the other does not.
  • A tool collapses a package that appears under several aliases or names.

Why completeness metadata matters

The CycloneDX specification includes compositions, which describe whether an inventory is complete, incomplete, or unknown. A tool that publishes this kind of statement tells you where it is unsure. When you compare tools, ask each one whether it reports completeness, and under what conditions it will say "incomplete". That honesty is often more valuable than a bigger number.

What to do with the result

Do not choose the tool with the biggest count, and do not choose the one with the smallest. Choose the one whose inventory you can explain, trace, and reproduce. If neither passes, ask the vendors for help diagnosing the gaps and watch how they respond. A vendor that can explain a discrepancy in your own repository within a day is showing you what support will look like.

Try it yourself

Click through it at your own pace, or open it full screen.

Look for how the inventory separates direct and indirect components, and think about which of those a count would include.

Quick check

Which tool is right if the counts differ?

Neither automatically. Compare definitions of scope, depth, and deduplication first, then trace the items that only one tool reports.

Should I normalize counts before comparing vendors?

Yes. Use the same scope, the same lockfiles, and a package-and-version key, or the comparison measures settings rather than quality.

Should I merge two tools' inventories into one?

Be careful. Merging hides which tool found what, and duplicates can inflate counts. Normalize to package identifiers first, and keep the source of each entry.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.