Open kuporific opened 3 years ago
Thanks for the detailed report. I've been able to reproduce. In the resulting bom, it appears that both versions of httpclient appear. Version 4.5.2 and 4.5.13 are both in the resulting bom, which is obviously not correct.
final ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration();
...
for (final ResolvedArtifact artifact : resolvedConfiguration.getResolvedArtifacts()) {
}
The gradle API method getResolvedArtifacts()
returns BOTH versions of the component. This is unexpected and varies from how most dependency management systems work. I would assume there's some missing logic that gradlew dependencies
implements that needs to be incorporated into this plugin. I'm just not sure what that is.
Playing with this scenario a bit, found that having the same dependency config type in the override seems to work. E.g. either of below will generate a bom with only httpclient@4.5.13
compile("com.amazonaws:amazon-sqs-java-messaging-lib:1.0.8") {
compile("org.apache.httpcomponents:httpclient:4.5.13") {
because "Older versions have CVEs"
}
}
implementation("com.amazonaws:amazon-sqs-java-messaging-lib:1.0.8") {
implementation("org.apache.httpcomponents:httpclient:4.5.13") {
because "Older versions have CVEs"
}
}
Also, gradle docs seem to favour use of constraints to control transitive dependency versions (albeit this is probably only available in recent versions) - https://docs.gradle.org/current/userguide/dependency_constraints.html
Thanks @fanovilla, seems kind of obvious in hindsight not to mix the Gradle configuration types. That did the trick for me.
Would you consider the description as good enough (and close) or will there be activity for improvement?
The problems here are that the Gradle snippet is not doing what you think it does, coupled with how the CycloneDX plugin works.
First,
dependencies {
compile("foo") {
implementation("bar")
}
}
actually works exactly the same as
dependencies {
compile("foo")
implementation("bar")
}
so there's no "overriding for a given transitive dependency" in this case (Gradle provides some means of doing such things, but that's not what's done here), but just adding a direct dependency with a more recent version so Gradle will prefer it over the transitive one.
Now couple that with how the plugin resolves each configuration that can be resolved separately, which means resolving e.g. compileClasspath
(which extends implementation
, which in turn extends compile
) separately from, say, runtime
(which extends compile
directly, without extending implementation
; runtimeClasspath
does extend both though). So when resolving a configuration that extends implementation
, you'll get the new direct dependency that's preferred over the older transitive one; but when resolving a configuration that extends compile
without also extending implementation
you don't get the direct dependency and thus have the "original" version of the transitive.
As a workaround, configure the Gradle configurations you want CycloneDX to resolve/process (e.g. includeConfigs = ["runtimeClasspath"]
if you want a SBOM only including the runtime dependencies)
Gradle allows you to specify a version of a transitive dependency if, for example, you need to use a newer version due to a 3rd party vulnerability. However, the generated BOM does not reflect the overridden version.
Here is a complete example:
build.gradle
settings.gradle
To demonstrate, if I run
./gradlew dependencies
, it shows that the transitive dependency version has been overridden with the desired version (as expected):Furthermore, the
build/publications/example/pom-default.xml
that's generated by running./gradlew generatePomFileForExamplePublication
also shows the overridden version (as expected):(Note that Maven dependency resolution uses the "nearest first" strategy while Gradle selects the highest version, so in any case, the desired overridden version will be used instead of the old vulnerable one.)
However, the
build/reports/bom.xml
generated by./gradlew cyclonedxBom
shows the wrong version (unexpected behavior):I believe this is a bug in the CycloneDx Gradle plugin, and that the BOM should reflect the overridden version.