SBOM & Compliance

Java SBOM Generation Tools Compared

Six tools generate SBOMs from Java projects. They disagree on transitive depth, license fields, and licensing of their own output. A head-to-head.

Shadab Khan
Security Engineer
5 min read

If you ship Java, you need an SBOM. The regulatory pressure from EU CRA, federal procurement, and enterprise customer questionnaires has moved from "would be nice" to "required in 2025." The tooling has kept up, more or less, but the tools disagree on enough dimensions that picking the wrong one for your project causes friction that outlasts the initial choice. This post is a comparison of the six Java SBOM generators that actually get used in production, with the dimensions that matter and the specific failure modes of each.

The six tools in scope

cyclonedx-maven-plugin — Official CycloneDX project; Maven-native. cyclonedx-gradle-plugin — Same project, for Gradle. Syft (Anchore) — Language-agnostic; scans filesystems and container images. Microsoft SBOM Tool — Microsoft's internal-turned-OSS generator, SPDX-focused. OWASP Dependency-Track — Not a generator itself, but consumes and manages generated SBOMs. sbom-maven-plugin (Spring-specific) — Less widely used but handles specific Spring Boot quirks.

Plus honourable mentions: JFrog's built-in SBOM export for Artifactory-hosted artifacts, and GitHub's dependency-graph-based SBOM export.

Dimension 1: transitive depth accuracy

The most important dimension. An SBOM that captures only direct dependencies is not an SBOM; it is a shopping list.

  • cyclonedx-maven-plugin with makeAggregateBom captures the full transitive graph. Depth is complete.
  • cyclonedx-gradle-plugin with recent versions (1.8+) is complete. Earlier versions had gaps around plugin-declared dependencies.
  • Syft captures transitives when scanning a JAR's lib/ or when handed a resolved lockfile-equivalent. Binary-only scanning misses shaded/relocated classes.
  • Microsoft SBOM Tool handles Maven and Gradle via build file scanning. Transitive capture is good but format is SPDX, which some consumers prefer.
  • sbom-maven-plugin is Spring-aware but limited.

For pure transitive accuracy, the CycloneDX official plugins are the baseline. Use them unless you have a specific reason otherwise.

Dimension 2: license field quality

License data in SBOMs is frequently wrong. Different tools extract it from different sources.

  • cyclonedx-maven-plugin reads <license> from POM files. If the POM declares a license, the SBOM carries it. If the POM doesn't, the field is empty.
  • Syft can do deeper inspection, including reading LICENSE files inside JARs. This catches cases where the POM is silent but the JAR contains the license.
  • Microsoft SBOM Tool reads POM licenses similarly to the CycloneDX plugin.

For license accuracy in an enterprise context, a post-processing step that reconciles POM declarations with actual LICENSE files is often needed regardless of generator choice.

Dimension 3: output format

  • CycloneDX 1.5 is the current CycloneDX spec. The plugins emit it by default.
  • SPDX 2.3 is what Microsoft SBOM Tool produces by default. SPDX 3.0 is published but less widely supported by consumers.
  • Both formats cover the baseline information needs. CycloneDX has deeper support for security-specific fields (VEX integration, vulnerability references). SPDX has deeper support for license-specific fields.

Federal procurement accepts both. EU CRA accepts both. The choice is usually a downstream consumer preference.

Dimension 4: integration into the build

  • cyclonedx-maven-plugin: add to POM, bind to the package phase, SBOM is a build output.
  • cyclonedx-gradle-plugin: add to build.gradle.kts, SBOM is generated alongside JARs.
  • Syft: run as a separate CLI step against built artifacts. More flexible but separate.
  • Microsoft SBOM Tool: separate CLI, runs against a directory or package.

Build-integrated generation (the CycloneDX plugins) has the advantage of being automatic. Separate-CLI generation has the advantage of being able to scan artifacts that have already been built, including ones you don't own the source for.

Dimension 5: handling of shaded/fat JARs

Spring Boot and many enterprise applications produce uber-JARs that relocate or shade dependencies. This breaks pure-Maven SBOM generation because the shaded classes don't match their original Maven coordinates.

  • cyclonedx-maven-plugin: misses shaded classes unless the Maven Shade plugin is configured to emit metadata the CycloneDX plugin can read.
  • Syft: can inspect the JAR contents directly and recover some shaded dependencies by hashing.
  • Microsoft SBOM Tool: handles this case better than pure-Maven plugins.

For Spring Boot specifically, running both a build-time CycloneDX generator and a post-build Syft scan, and reconciling the outputs, catches the shading problem. It's redundant but worth it for Spring-heavy portfolios.

Dimension 6: ongoing maintenance

The CycloneDX plugins are maintained by the OWASP CycloneDX project with active development. Syft is maintained by Anchore with a strong release cadence. Microsoft SBOM Tool has been stable but has a slower release cadence in 2024 than the others.

For projects that need tooling to keep up with spec evolution (CycloneDX 1.5 → 1.6, SPDX 2.3 → 3.0), active maintenance matters.

When to use each

  • Pure Maven project, standard workflow: cyclonedx-maven-plugin.
  • Pure Gradle project: cyclonedx-gradle-plugin.
  • Spring Boot with shaded JARs: cyclonedx-maven-plugin + post-build Syft reconciliation.
  • Scanning pre-built or third-party JARs: Syft.
  • Microsoft-aligned toolchain, SPDX preference: Microsoft SBOM Tool.
  • Multi-language monorepo: Syft as primary.
  • Production SBOM management and consumer-facing: OWASP Dependency-Track ingesting from any of the above.

Practical configuration for Maven

<plugin>
  <groupId>org.cyclonedx</groupId>
  <artifactId>cyclonedx-maven-plugin</artifactId>
  <version>2.8.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>makeAggregateBom</goal>
      </goals>
      <configuration>
        <outputFormat>all</outputFormat>
        <outputName>bom</outputName>
        <includeCompileScope>true</includeCompileScope>
        <includeProvidedScope>true</includeProvidedScope>
        <includeRuntimeScope>true</includeRuntimeScope>
        <includeSystemScope>true</includeSystemScope>
        <includeTestScope>false</includeTestScope>
      </configuration>
    </execution>
  </executions>
</plugin>

Practical configuration for Gradle

plugins {
    id("org.cyclonedx.bom") version "1.10.0"
}

tasks.named("cyclonedxBom", org.cyclonedx.gradle.CycloneDxTask::class.java).configure {
    setIncludeConfigs(listOf("runtimeClasspath"))
    setProjectType("application")
    setSchemaVersion("1.5")
    setIncludeLicenseText(false)
}

How Safeguard Helps

Safeguard ingests Java SBOMs from any of the generators above and normalises them into a single supply chain graph, so multi-tool portfolios (Maven + Gradle + Syft for shaded JARs) present a unified view. Griffin AI identifies gaps where a generator's output is missing expected information (empty license fields, missing transitives due to shading) and suggests which complementary tool closes the gap. Policy gates can require SBOM attachment on every release artifact and fail builds that do not produce one. For Java organisations standardising on SBOM practice in 2024, Safeguard removes the per-generator tax and makes SBOM-quality enforcement a platform property.

Never miss an update

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