SBOM+ Maven Plugin

An SBOM of everything your build actually uses

A Maven plugin that writes a CycloneDX SBOM covering the resolved project dependency graph, every build plugin and its transitive dependencies, and the BOMs you import. Not just the application classpath.

Version 1.0.0 CycloneDX 1.5 Maven 3.9 Java 8+ Apache 2.0

What it finds

Main build graph

The fully resolved project dependency graph across compile, runtime, provided and test scopes, with the real path that brought each artifact in.

Build tooling

Every plugin in the effective build, plus each plugin's own transitive dependencies and any extra <plugin><dependencies>. These run arbitrary code during your build and never show up in the project classpath.

BOM imports

The import-scoped POMs from <dependencyManagement>, since a BOM's release train is a trackable component in its own right.

The plugin only enumerates and writes. It does no vulnerability, end-of-life or licence annotation. Feed the SBOM to whatever tooling does that for you.

Quick start

Run it against any Maven project without touching the POM:

mvn dev.noregressions:sbom-plus-maven-plugin:1.0.0:scan

For a multi-module reactor, run the aggregate goal once from the root:

mvn dev.noregressions:sbom-plus-maven-plugin:1.0.0:scan-aggregate

Or bind it into the build so every mvn verify produces an SBOM:

<plugin>
  <groupId>dev.noregressions</groupId>
  <artifactId>sbom-plus-maven-plugin</artifactId>
  <version>1.0.0</version>
  <executions>
    <execution>
      <goals>
        <goal>scan</goal>
      </goals>
    </execution>
  </executions>
</plugin>
Both goals do their own dependency resolution, so they work as a bare goal against a checkout that has never been built. See unresolved dependencies for what happens when something cannot be found.

Goals and parameters

sbom-plus:scan

Scans the current module only. Binds to the verify phase by default.

ParameterPropertyDefault
outputFilesbomPlus.outputFile${project.build.directory}/sbom-plus.json
sbomOutputFilesbomPlus.sbomOutputFile${project.build.directory}/sbom-plus-cyclonedx.json

sbom-plus:scan-aggregate

Runs once for a whole multi-module reactor and writes one combined report and one combined SBOM at the top level.

ParameterPropertyDefault
outputFilesbomPlus.outputFile${session.topLevelProject.build.directory}/sbom-plus-aggregate.json
sbomOutputFilesbomPlus.sbomOutputFile${session.topLevelProject.build.directory}/sbom-plus-cyclonedx-aggregate.json

Override either path from the command line:

mvn dev.noregressions:sbom-plus-maven-plugin:1.0.0:scan -DsbomPlus.sbomOutputFile=build/sbom.json

Outputs

Each goal writes two files.

The CycloneDX SBOM

A CycloneDX 1.5 JSON document with one library component per distinct artifact. Only artifacts that actually resolved are listed. Each component carries:

The document also has a dependencies graph rebuilt from the real resolution paths, and a metadata.tools entry naming the plugin and its version. Every SBOM the test suite produces is validated against the official CycloneDX schema.

The scan report

The plugin's own richer, per-module view. For every row it records which resolution pass found it, its scope, and the root-first path that brought it in.

{
  "formatVersion" : 1,
  "generatedAt" : "2026-09-06T12:29:53Z",
  "modules" : [ {
    "module" : "com.example:app",
    "dependencies" : [ {
      "coordinate" : { "groupId" : "org.slf4j", "artifactId" : "slf4j-api", "version" : "2.0.13" },
      "origin" : "MAIN_BUILD",
      "scope" : "compile",
      "type" : "jar",
      "module" : "com.example:app",
      "broughtInBy" : [ { "groupId" : "ch.qos.logback", "artifactId" : "logback-classic", "version" : "1.5.6" } ]
    } ],
    "unresolved" : [ ]
  } ]
}

origin is one of MAIN_BUILD, BUILD_TOOLING or BOM. formatVersion is bumped whenever the report shape changes in a way consumers need to know about.

Unresolved dependencies

A dependency that cannot be resolved does not fail the build. The typical case is a multi-module reactor scanned before it has ever been built, where one module depends on a sibling SNAPSHOT that exists nowhere yet. In that situation the plugin:

"unresolved" : [ {
  "coordinate" : { "groupId" : "com.example", "artifactId" : "core", "version" : "1.2.0-SNAPSHOT" },
  "scope" : "compile",
  "reason" : "Could not find artifact com.example:core:jar:1.2.0-SNAPSHOT"
} ]

Requirements

Source and issues

Source, issue tracker and release notes live at github.com/noregressions/sbom-plus-maven-plugin. Build it locally with mvn verify, which runs the unit tests and the forked integration builds under src/it.