SBOM Completeness with Direct & Transitive Dependencies

Arunanshu Biswas 5 min read

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.

Background

Since its earliest manuals, Maven has promised “automatic updating” and “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 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.

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). Industry analysis of a million of Java packages 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 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.

Problem: SBOMs That Stop at the First Branch

Maven hides the full dependency tree

Maven resolves transitive dependencies automatically; there is “no limit to the number of levels that dependencies can be gathered from”. The popular dependency: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. Studies of real-world incidents show that updating a direct library often fails to patch flaws 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 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 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 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. OpenSSF notes, it also obscures licence inheritance, exposing organisations to unexpected LGPL or GPL obligations.

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 produces a canonical graph that downstream tools can ingest. For automation, the CycloneDX 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 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 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.

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 caseDirects onlyTransitivesGraph exportNotable quirks
Syft⚠️ (Java needs SYFT_JAVA_RESOLVE_TRANSITIVE_DEPENDENCIES)❌ (flat list)Includes test scope by default
CycloneDX Maven Pluginvia dependencyGraphRuns in-build, no multi-ecosystem
SPDX-toolsN/AFocus on license data
SafeDep Vet--report-graphMulti-lang with policy engine
OSV ScannerTransitive Maven scanning by default; —no-resolve disables transitives; no native graph export

Protect Against Malicious Open Source Packages

Don't let supply chain attacks compromise your projects. SafeDep Vet helps you identify and prevent malicious packages before they enter your codebase.

Back to Blog

Related Posts

View All Posts »
Dynamic Malware Analysis of Open Source Packages at Scale

Dynamic Malware Analysis of Open Source Packages at Scale

Exploring the idea of building a complementary system that can verify and correlate static analysis findings. Thats where dynamic analysis comes in ie. the ability to "run" an open source package in an observed environment and determine its safety status based on real behavior at runtime

Secure Vibe Coding with AI Agents

Secure Vibe Coding with AI Agents

AI coding agents make development faster but can inadvertently introduce security risks by suggesting unvetted packages. Learn how to use vet MCP server for adding security to your vibe coding adventures.