Most Java teams that think they have an SBOM actually have half of one. The org.cyclonedx:cyclonedx-maven-plugin, now at version 2.9.2 as of June 2026, can generate a CycloneDX bill of materials with a single makeAggregateBom goal, and the Gradle equivalent — applied via the org.cyclonedx.bom plugin id from the CycloneDX GitHub org — does the same in a build/reports/ output directory. Both are free, both are well-maintained, and both run in under a minute on most projects. The gap isn't tooling access; it's that a document listing package names and versions was already considered "complete" by most teams in 2021, when NTIA's July 2021 Minimum Elements framework defined seven required fields. CISA's August 2025 public-comment draft proposes four more fields on top of that baseline — Component Hash, License, Tool Name, and Generation Context — which would bring the count to eleven if adopted as published. That draft is pre-decisional and not yet final U.S. government policy, but it's the direction the bar is moving, and it's worth building toward now. An SBOM generated with default plugin settings, run before a full mvn package, against a multi-module repo without aggregate mode enabled, will typically satisfy fewer than half of those eleven fields. This post walks through generating a CycloneDX or SPDX SBOM from a real Maven or Gradle build, then validating it against that emerging eleven-field bar instead of the 2021 seven-field one.
How do you generate a CycloneDX SBOM with Maven?
Add the org.cyclonedx:cyclonedx-maven-plugin (2.9.2) to your pom.xml build plugins section and bind it to a lifecycle phase such as package, then run mvn cyclonedx:makeAggregateBom for a multi-module project or mvn cyclonedx:makeBom for a single module. The plugin resolves your dependency graph — direct and transitive — and writes both bom.json and bom.xml under target/, following the CycloneDX 1.5+ schema published by the CycloneDX project. The critical detail teams miss: running the goal against mvn compile instead of a full mvn package (or verify) means Maven hasn't fully resolved every dependency scope, and the resulting BOM silently omits test-scoped or provided-scope artifacts that still ship in some deployment contexts. For multi-module repositories, makeBom run per-module produces N separate, incomplete BOMs — makeAggregateBom at the parent POM is what actually captures cross-module dependency relationships in one document.
How do you generate a CycloneDX SBOM with Gradle?
Apply the org.cyclonedx.bom plugin, maintained by the CycloneDX GitHub organization at CycloneDX/cyclonedx-gradle-plugin, in your build.gradle or build.gradle.kts, then run ./gradlew cyclonedxBom. Output lands under build/reports/ as bom.json and bom.xml by default. Unlike the Maven plugin, Gradle's dependency resolution is configuration-based, so the BOM's completeness depends on which configurations you tell the plugin to scan — a configurations block that only lists runtimeClasspath will miss testRuntimeClasspath or annotationProcessor dependencies entirely. This matters for supply-chain risk specifically because annotation processors (Lombok, MapStruct, Dagger) run arbitrary code at compile time and have their own CVE history, yet they're one of the most commonly excluded configurations in default plugin setups.
How do you generate an SPDX SBOM instead of CycloneDX?
For Maven, the spdx-maven-plugin, maintained under the SPDX organization, generates tag-value and JSON SPDX documents via a bound createSPDX goal, walking the same resolved dependency tree the CycloneDX plugin does. Gradle has no equally canonical first-party SPDX plugin — this is a real and persistent gap, not an oversight on your part — which is why CycloneDX is the more common default choice for Gradle-built Java projects, with teams that need SPDX specifically often converting a CycloneDX document afterward or running Maven-based SPDX tooling against a Gradle project's resolved pom.xml equivalent. If your compliance regime mandates SPDX (some U.S. federal contracts and the EU Cyber Resilience Act's SBOM-adjacent obligations lean toward SPDX or CycloneDX interchangeably), confirm which format your downstream consumer actually parses before picking a toolchain around it.
What are the NTIA and CISA minimum elements, and does your SBOM actually contain them?
NTIA's July 2021 "Minimum Elements for a Software Bill of Materials" defined seven required fields: Supplier Name, Component Name, Version, Other Unique Identifiers (such as a PURL or CPE), Dependency Relationship, Author of SBOM Data, and Timestamp. CISA's August 2025 public-comment draft of an updated Minimum Elements document proposes four more fields — Component Hash, License, Tool Name, and Generation Context — which would bring the working baseline to eleven fields once finalized; treat that eleven-field bar as the direction of travel rather than a settled requirement today. A default cyclonedx-maven-plugin run populates PURLs, versions, and component names reliably, but license fields are only populated when license metadata exists in the upstream artifact's POM — many older or internally published Maven artifacts simply don't declare one, leaving that field blank in your generated BOM even though the plugin ran correctly. Component hashes are included by default in CycloneDX output but require an explicit includeBomSerialNumber and hash-algorithm configuration in SPDX generation to appear consistently.
How do you validate that a generated SBOM is actually complete?
The SPDX project maintains spdx/ntia-conformance-checker, an open-source command-line tool that parses an SPDX document and reports, field by field, whether it satisfies NTIA/CISA minimum-element conformance — run it against your spdx-maven-plugin output as a concrete pass/fail gate rather than eyeballing the JSON. For CycloneDX documents, there's no single canonical NTIA-conformance checker maintained by the CycloneDX project itself, so teams typically validate structurally against the published CycloneDX JSON schema first, then spot-check for the license and hash gaps described above. Either way, treat SBOM generation and SBOM validation as two separate pipeline steps — a build that "succeeds" at running mvn cyclonedx:makeAggregateBom tells you the plugin executed, not that the output satisfies the seven fields NTIA already requires or the additional fields CISA's draft update is pushing toward.
How Safeguard Helps
Once you've generated a CycloneDX or SPDX document from your Maven or Gradle build, Safeguard's ESSCM lets you upload that SBOM directly — via the Upload Manifest integration or CI-driven ingestion — and view it through the Project Overview page rather than trusting the plugin's exit code alone. The Dependencies tab renders the full resolved tree with license and version data per component, so a missing license field from an undeclared upstream POM shows up as a visible gap instead of a silent one, and the Attestation tab layers in SLSA provenance and Sigstore signature checks that neither the CycloneDX nor SPDX Maven/Gradle plugins attempt on their own. Safeguard also exports that same ingested SBOM back out in more than 30 formats, including SPDX (JSON, XML, RDF) and CycloneDX (JSON, XML), so a document generated by cyclonedx-maven-plugin for your own build pipeline can be re-shaped for whichever format a customer or auditor actually requires — one queryable inventory instead of a fresh mvn run every time the target format changes.