---
title: "SBOM Completeness with Direct & Transitive Dependencies"
description: "Hidden transitive dependencies create security blind spots. This blog\nshows developers and CISOs how SafeDep vet uncovers full Maven dependency\ngraphs, generating CycloneDX SBOMs and compliance-ready visuals.\n"
url: "https://safedep.io/blog/sbom-direct-transitive-deps"
author: "Arunanshu Biswas"
published: "2025-07-05"
---

# SBOM Completeness with Direct & Transitive Dependencies

While modern SBOMs are all about making dependencies and tooling "visible", many
of them are still blind: they list only the libraries you specified in your
`pom.xml` and skip the dense forest of dependency trees that actually lands in
production. That blind spot is where malware, outdated libs, and licensing
issues like to hide. In this blog post, we will discuss why capturing both
direct **and** transitive dependencies matters, why Maven projects are
especially tricky, and how you can build a complete CycloneDX SBOM (with a
visual graph!) out of SafeDep Vet in one command. Along the way we compare
mainstream generators, map the regulatory pressure from laws like EU CRA, and
come up with a plan-of-action for devs/CISOs for closing the gaps.

If you are interested in learning more about EU CRA, please refer to our blog
[SBOM and the EU Cyber Resilience Act (CRA) – What Software Vendors Need to Know](https://safedep.io/blog/sbom-and-eu-cra-cyber-resilience-act).

## Background

Since its earliest manuals, Maven has promised "automatic updating" and
["dependency closures"][maven-dependency-closures] (also known as transitive
dependencies), meaning that a entry in `pom.xml` silently expands into
every library required by that depencency and its descendants. The
[official docs][maven-no-limit] explains that there is "no limit to the number
of levels that dependencies can be gathered from," with resolution performed
recursively at build time and the generated Project Dependencies pages regularly
show dozens of jars that never appear in the root POM. However, unlike build
systems and package managers like `pnpm`, `cargo`, `pdm` etc., Maven and the
Java ecosystem as a whole
[lacks a solid foundation for lockfiles][maven-no-lockfiles].

While this abstraction speeds development, it also conceals risk: unwanted or
outdated libraries can ride in unless a maintainer explicitly excludes them (eg,
[optional dependencies][maven-opt-dep]).
[Industry analysis of a million of Java packages][java-trans-dep-analysis] found
that almost one-third of exploitable vulnerabilities live only in transitive
layers, invisible to tools that stop at first-level imports.
[The Log4Shell crisis][log4shell-crisis] illustrated the danger when
organisations were compromised by an indirect copy of `log4j-core` they never
declared themselves, and attackers continue to probe that vector years later.

[maven-no-lockfiles]: https://stackoverflow.com/q/44521542/11663108
[maven-dependency-closures]: https://maven.apache.org/what-is-maven.html
[maven-opt-dep]: https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html
[java-trans-dep-analysis]: https://arxiv.org/abs/2301.07972
[log4shell-crisis]: https://www.datadoghq.com/blog/log4j-log4shell-vulnerability-overview-and-remediation/

## Problem: SBOMs That Stop at the First Branch

### Maven hides the full dependency tree

Maven [resolves transitive dependencies automatically][maven-no-limit]; there is
"no limit to the number of levels that dependencies can be gathered from". The
popular [`dependency:tree` goal][maven-dep-tree-goal] shows that expansion in
text, DOT or JSON formats, often revealing 10–50x more artefacts than appear in
the root POM. Research on 3 million Maven packages confirms that almost
one-third of projects are _only_ vulnerable if those deeper layers are considered.

### Risk concatanates in transitive layers

The Log4Shell exploit entered many Java applications that never declared Log4j
directly; [it arrived through indirect dependencies][log4j-transitive-dep].
Studies of real-world incidents show that updating a direct library often
[fails to patch flaws][patch-flaw-graph] buried further down the graph, and
tooling that stops at the first level cannot surface those blind spots.

### Many SBOM generators still abbreviate

[Issues filed against Syft][syft-java] report that versions prior to 1.30 listed
only first-level packages for Java, omitting the dependency relationships
required by auditors. However, NTIA’s minimum-elements document treats "X is
included in Y" as the [single mandatory relationship][ntia-transitive-dep] every
SBOM must capture.

### Regulatory momentum demands completeness

The EU Cyber Resilience Act will require vendors shipping to Europe to provide
machine-readable SBOMs that cover complete dependency chains by December 2027,
with fines up to €15 million for non-compliance. In the United States,
[Executive Order 14028][eo-14028] pushes federal agencies to buy only software
that comes with an SBOM aligned to NIST guidance. Partial inventories will
therefore fail procurement checks.

### Operational impact for developers and CISOs

A flat SBOM hides which direct upgrade would excise a vulnerable transitive
package, prolonging incident response times. In other words, a flat SBOM is
[ineffective in capturing the transitive dependencies][sbom-not-capture-trans-dep].
[OpenSSF notes][sbom-openssf], it also obscures licence inheritance, exposing organisations to unexpected LGPL or GPL obligations.

[maven-no-limit]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#:~:text=There%20is%20no%20limit%20to%20the%20number%20of%20levels%20that%20dependencies%20can%20be%20gathered%20from.%20A%20problem%20arises%20only%20if%20a%20cyclic%20dependency%20is%20discovered.
[maven-dep-tree-goal]: https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html
[log4j-transitive-dep]: https://www.crowdstrike.com/en-us/cybersecurity-101/exposure-management/log4j-vulnerability/#:~:text=Its%20flexibility%20and%20ease%20of%20integration%20made%20it%20the%20default%20logging%20framework%20for%20many%20Java%20applications%2C%20and%20it%20was%20often%20bundled%20deep%20within%20software%20stacks%20via%20transitive%20dependencies.
[patch-flaw-graph]: https://www.arnica.io/blog/direct-vs-transitive-dependencies-navigating-package-management-in-software-composition-analysis-sca
[ntia-transitive-dep]: https://www.ntia.gov/files/ntia/publications/sbom_minimum_elements_report.pdf
[syft-java]: https://github.com/anchore/syft/issues/3189
[eo-14028]: https://www.whitehouse.gov/wp-content/uploads/2022/09/M-22-18.pdf
[sbom-not-capture-trans-dep]: https://semgrep.dev/blog/2024/overrated-and-underperforming-transitive-reachability-analysis/
[sbom-openssf]: https://openssf.org/blog/2023/06/20/why-sbom-generators-need-to-accurately-represent-open-source-licenses/

## Solution -- Capture the Whole Graph

However, there are some ways to get around this situation, without having to
switch to Gradle.

### Extract the authoritative graph from Maven

Since maven already computes the full resolution, make use of it. The
`mvn dependency:tree -DoutputType=dot` [command][maven-dep-tree-goal] produces a
canonical graph that downstream tools can ingest. For automation, the
[CycloneDX Maven plugin][cdx-maven-plugin] emits a CycloneDX v1.6 SBOM that
aggregates all direct and transitive dependencies in a single step.

### Prefer graph-aware SCA tools

[SafeDep Vet][safedep-vet] resolves the Maven tree, and writes a CycloneDX SBOM
plus an optional DOT graph in one CLI invocation, eliminating manual glue code.
By contrast, Syft requires environment flags for Java and still lacks complete
graph output in certain modes, as its
[own issue tracker][syft-java-issue-tracker] notes.

### Embed verification in CI/CD

Add a pipeline step that:

1. Generates `bom.json` (CycloneDX) and `deps.dot` (graph) at build time.
2. Validates the SBOM.
3. Fails the build if any critical CVE is detected anywhere in the graph.

This satisfies CRA’s upcoming requirement for continuous vulnerability
monitoring across the full supply chain.

And voila! Because the SBOM now contains edge data, remediation tools can
pinpoint the closest direct dependency upgrade that eliminates a vulnerable
indirect library, shortening mean-time-to-patch and reducing the risk of
breaking upstream APIs.

[cdx-maven-plugin]: https://cyclonedx.github.io/cyclonedx-maven-plugin/project-info.html
[safedep-vet]: https://github.com/safedep/vet
[syft-java-issue-tracker]: https://github.com/anchore/syft/issues/572

## Tools Landscape Overview

The landscape analysis spans Maven-native plugins that output CycloneDX files
with full transitive coverage, open-source scanners such as Syft that are still
refining their Java dependency-graph support, SPDX-focused licence tools, and
SafeDep’s Vet, which combines graph-complete SBOMs with malware and policy
checks. The table below compares their default abilities, export formats, and
known quirks:

| Use case                       | Directs only | Transitives                                                 | Graph export          | Notable quirks                                                                                  |
| ------------------------------ | ------------ | ----------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------- |
| **Syft**                       | ✅           | ⚠️ (Java needs `SYFT_JAVA_RESOLVE_TRANSITIVE_DEPENDENCIES`) | ❌ (flat list)        | [Includes _test_ scope by default][anchore-syft-test-scope]                                     |
| **CycloneDX Maven Plugin**     | ✅           | ✅                                                          | via `dependencyGraph` | Runs in-build, no multi-ecosystem                                                               |
| **SPDX-tools**                 | ✅           | ❌                                                          | N/A                   | Focus on license data                                                                           |
| [**SafeDep Vet**][safedep-vet] | ✅           | ✅                                                          | `--report-graph`      | Multi-lang with policy engine                                                                   |
| [**OSV Scanner**][osv-scanner] | ❌           | ✅                                                          | ❌                    | Transitive Maven scanning by default; --no-resolve disables transitives; no native graph export |

[anchore-syft-test-scope]: https://anchorecommunity.discourse.group/t/java-transitive-dependencies/224
[osv-scanner]: https://github.com/google/osv-scanner
