Set includeConfigs and skipConfigs on the CycloneDX Gradle direct task. By default the plugin scans every resolvable configuration, which pulls test and tooling dependencies into the SBOM. List runtimeClasspath and compileClasspath to include, and skip anything matching test, so the SBOM reflects what ships.
Why the default surprises people
Gradle organizes dependencies into configurations: groups such as compile, runtime and test classpaths. The plugin README states that a direct SBOM scans every resolvable project configuration by default. Test frameworks, linters and build helpers can therefore show up beside the libraries you actually ship.
That has two costs. Customers reading the SBOM see components that never run in production. And vulnerability matching produces findings against test only libraries, which adds noise to a triage queue.
Whether you want those entries depends on the audience. An internal inventory of everything the build touches is legitimate. A file sent to a customer should describe the shipped product. Decide per audience, and produce the right file for each.
The two properties
Both are lists of full match regular expressions.
| Property | Meaning |
|---|---|
| includeConfigs | Configurations to scan. Empty means every resolvable configuration |
| skipConfigs | Configurations to exclude. Exclusion takes precedence over inclusion |
Because matching is a full match, a pattern must cover the whole configuration name. A pattern of test alone will not match testRuntimeClasspath. That is why the README example uses a case insensitive pattern with wildcards on both sides.
An example
Kotlin DSL:
tasks.cyclonedxDirectBom {
includeConfigs = listOf("runtimeClasspath", "compileClasspath")
skipConfigs = listOf("(?i).*test.*")
}
Groovy DSL:
tasks.cyclonedxDirectBom {
includeConfigs = ['runtimeClasspath', 'compileClasspath']
skipConfigs = ['(?i).*test.*']
}
In a multi-project build, configuration belongs to each task and the plugin does not add an extension, so apply the setting to the direct task in every project that needs it, for instance from a subprojects block in the root build script.
Steps to get it right
- Generate the SBOM with defaults and save it.
- Add the two properties and generate again.
- Diff the two files. Everything that disappeared should be test or tooling only.
- If something that does ship disappeared, your include list is too narrow. Add its configuration name.
- Keep the settings in version control so the policy is reviewable.
Step 3 is the safety net. A pattern that is too aggressive can drop a runtime dependency, and an SBOM that omits shipped code is a worse problem than one that lists extra tooling.
When to keep test dependencies
Keep them in an internal build inventory if you want to know every component that executes on a developer machine or in CI, since a compromised test dependency can still run code in your pipeline. Many teams therefore produce two files: a shipped product SBOM for customers and a full build inventory for internal monitoring.
Safeguard can ingest both kinds and scan them for known vulnerabilities, with reachability analysis helping you see which findings sit in code your application actually calls. A free scan lets you try it on a project.
Quick check
Why did my pattern not match anything?
The patterns are full matches. A bare word will not match a longer configuration name, so wrap it in wildcards, for example with a leading and trailing wildcard.
Should customers get the test dependencies too?
Usually no. Send a shipped product SBOM and keep the broader build inventory internal, unless a contract asks otherwise.
Sources
Last reviewed: 19 October 2026.