How to fix Log4J Vulnerability in Gradle Project - spring

I can see org.apache.logging.log4j:log4j-core:2.14.0 Library in Gradle dependency tree for my project.
We have not added log4j version from outside. This version is coming as part of transitive dependencies from other jars or spring-boot-starter.
How to override the log4j version in Gradle?

First, find out which log4j-related libraries you are really using, e.g. by
.\gradlew dependencies --configuration=testRuntimeClasspath | find "log4j"
Then override them with a current version like so (docs), placed after the dependencies block:
configurations.all {
resolutionStrategy {
force 'org.apache.logging.log4j:log4j-api:2.17.0'
force 'org.apache.logging.log4j:log4j-core:2.17.0'
force 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.0'
force 'org.apache.logging.log4j:log4j-jul:2.17.0'
}
}
You might need to add more/fewer libraries to that block depending on the results of the check in the beginning.
Since you are using Spring Boot, you can also use a Spring-Boot-specific feature to set the Log4J version:
ext['log4j2.version'] = '2.17.0'

I'm on MacOS and using subprojects:
First I run: ./gradlew projects which will list my subprojects:
The output:
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'test-backend'
+--- Project ':test-suite'
+--- Project ':test-suite-services'
\--- Project ':test-utils'
Using the output we can one by one check for dependencies:
./gradlew test-suite:dependencies | grep "log4j"
./gradlew test-suite-services:dependencies | grep "log4j"
./gradlew test-utils:dependencies | grep "log4j"

https://docs.gradle.org/current/userguide/resolution_rules.html constrains are preferred.
https://docs.gradle.org/current/userguide/rich_versions.html#rich-version-constraints
https://blog.gradle.org/log4j-vulnerability
dependencies {
constraints {
implementation('org.apache.logging.log4j:log4j-api') {
version {
strictly('[2.17, 3[')
prefer('2.17.0')
}
because('CVE-2021-44228, CVE-2021-45046, CVE-2021-45105: Log4j vulnerable to remote code execution and other critical security vulnerabilities')
}
implementation('org.apache.logging.log4j:log4j-core') {
version {
strictly('[2.17, 3[')
prefer('2.17.0')
}
because('CVE-2021-44228, CVE-2021-45046, CVE-2021-45105: Log4j vulnerable to remote code execution and other critical security vulnerabilities')
}
implementation('org.apache.logging.log4j:log4j-slf4j-impl') {
version {
strictly('[2.17, 3[')
prefer('2.17.0')
}
because('CVE-2021-44228, CVE-2021-45046, CVE-2021-45105: Log4j vulnerable to remote code execution and other critical security vulnerabilities')
}
implementation('org.apache.logging.log4j:log4j-web') {
version {
strictly('[2.17, 3[')
prefer('2.17.0')
}
because('CVE-2021-44228, CVE-2021-45046, CVE-2021-45105: Log4j vulnerable to remote code execution and other critical security vulnerabilities')
}
}
}

Related

Gradle Dependency of Dependency Substitution

I have a spring-boot gradle project. It has a dependency of a dependency of a dependency that I cannot use for internal reasons. I've forked that module and published it to my repo with its own version number that I want to be used in my project.
Part of the dependency tree looks like:
compileClasspath - Compile classpath for source set 'main'.
+--- org.springframework.boot:spring-boot-starter-web:2.7.2
| +--- org.springframework.boot:spring-boot-starter-tomcat:2.7.2
| | +--- jakarta.annotation:jakarta.annotation-api:1.3.5
| | +--- org.apache.tomcat.embed:tomcat-embed-core:9.0.65
| | +--- org.apache.tomcat.embed:tomcat-embed-el:9.0.65
| | \--- org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65
I need to force my project to use a custom version of those bottom two modules, let's call the new versions org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM and org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM.
I've never attempted this. I'm trying all different setups in my project's build.gradle file like
configurations { main }
configurations.main.resolutionStrategy.dependencySubstitution {
substitute module('org.apache.tomcat.embed:tomcat-embed-el:9.0.65') using module('org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM')
substitute module('org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65') using module('org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM')
}
But the 9.0.65 versions keep getting injected. So my question(s) are:
Am I using dependencySubstitution incorrectly, and where in the build.gradle should it be?
Does dependencySubstitution work for dependencies of dependencies (ad nauseam), or only for direct dependencies?
Short of forking all these dependencies to declare their dependencies, is there any way I can force my Gradle to never use version 9.0.65 and only ever use 9.0.65.CUSTOM no matter its location in the dependency tree?
Assuming your configurations are setup properly and the coordinates for your custom modules are correct, you should be able to do
allprojects {
configurations.all {
resolutionStrategy {
dependencySubstitution {
substitute(module("org.apache.tomcat.embed:tomcat-embed-el")).with(module("org.apache.tomcat.embed:tomcat-embed-el:9.0.65.CUSTOM"))
substitute(module("org.apache.tomcat.embed:tomcat-embed-websocket")).with(module("org.apache.tomcat.embed:tomcat-embed-websocket:9.0.65.CUSTOM"))
}
}
}
}
I would double check you have your repositories, configurations and that you are doing this dependency substitution in the module that needs to substitute the given modules for custom ones. Otherwise you may run into errors.

Specify Gradle version constraints for multiple configurations

I have a multi-module Gradle 6.5 project which also includes some test fixtures code. To avoid dependency issues I'd like to set (and maintain) the versions in one place, and just refer to the version-less dependencies in the individual modules:
subprojects {
apply plugin: 'java'
dependencies {
implementation 'com.google.guava:guava'
constraints {
implementation 'com.google.guava:guava:20.0'
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
}
}
}
Now, if a module includes compilyOnly 'com.google.code.findbugs:jsr305' the version 3.0.2 is taken based on the constraints above. This works fine if I know the configuration (implementation, compileOnly, ...).
The question now is: How can I specify versions that apply to all configuration? What if a module decides to use JSR305 annotations for test fixtures code? testFixtures 'com.google.code.findbugs:jsr305' fails, as the version is not specified anywhere. I also think it's a bad idea to repeat the version specification for all (possible) configuration:
implementation 'com.google.code.findbugs:jsr305:3.0.2'
testFixturesImplementation 'com.google.code.findbugs:jsr305:3.0.2'
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
Is there a shortcut to this?
If fixed this using the java-platform plugin.
Platform:
plugins {
id 'java-platform'
}
dependencies {
constraints {
api 'com.google.guava:guava:20.0'
api 'com.google.code.findbugs:jsr305:3.0.2'
}
}
Regular module with test fixtures:
plugins {
id "java-test-fixtures"
}
dependencies {
testFixturesImplementation platform(project(':platform-module'))
testFixturesCompileOnly 'com.google.code.findbugs:jsr305'
}
./gradlew -q module-with-test-fixtures:dependencies gives:
[...]
testFixturesCompileClasspath - Compile classpath for source set 'test fixtures'.
+--- project :module-with-test-fixtures (*)
+--- com.google.code.findbugs:jsr305 -> 3.0.2
\--- project :platform-module
\--- com.google.code.findbugs:jsr305:3.0.2 (c)
[...]
(c) - dependency constraint

Gradle plugins' transitive dependencies interfering and breaking the build

I am using Gradle 6.1 in a multimodule project. I am also using two plugins: kotlin("jvm") and id("com.google.cloud.tools.jib"), and they are loaded in the following modules:
root/
build.gradle.kts loads kotlin("jvm")
services/
my-service/
rest/
build.gradle.kts loads id("com.google.cloud.tools.jib")
(There are more modules, files etc. but these are the relevant ones.)
The build fails:
$ ./gradlew clean jibDockerBuild
...
* What went wrong:
Execution failed for task ':services:driver:rest:jibDockerBuild'.
> com.google.cloud.tools.jib.plugins.common.BuildStepsExecutionException: 'org.apache.http.client.config.RequestConfig$Builder
org.apache.http.client.config.RequestConfig$Builder.setNormalizeUri(boolean)'
I identified the issue: both the Kotlin and JIB plugins have a transitive dependency on org.apache.httpcomponents:httpclient: Kotlin requires 4.5.3 and JIB 4.5.10. The problem is, in this project setup only 4.5.3 is loaded, and JIB fails as the new method is not available. This can be checked with ./gradlew buildEnv.
I've found a workaround, I need to load both plugins at the root level (which one is first seems to be irrelevant) in the main Gradle file; now ./gradlew buildEnv shows that the higher dependency version is used, also for Kotlin (output shortened and incomplete):
classpath
+--- org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.3.61
| \--- org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61
| +--- de.undercouch:gradle-download-task:3.4.3
| | \--- org.apache.httpcomponents:httpclient:4.5.3 -> 4.5.10
It works in this case, but it could be that the new library version breaks the Kotlin plugin. The problem is that the plugins and their dependencies are on the classpath without separation, something that was normal on Java before Jigsaw etc. Is there any way for Gradle to be able to separate the dependencies so that each plugin uses exactly the version it declares? I am building on Java 11, so the module system could be utilized, but does Gradle have an option to turn it on?
EDIT: updating to Kotlin 1.3.70 also fixes the issue as it doesn't depend on the library any longer. The general question is still valid, though.
Is there any way for Gradle to be able to separate the dependencies so that each plugin uses exactly the version it declares
No.
All plugins share the same build script configuration: classpath
It follows the same dependency resolution that application dependencies follow. So you can enforce that for this particular dependency only use a specific version always:
buildscript {
configurations {
classpath {
resolutionStrategy {
force("org.apache.httpcomponents:httpclient:4.5.10")
}
}
}
}
That's just one of many ways you can take control of dependency resolution for build script dependencies. You could also use a platform to advise on the dependency versions:
buildscript {
dependencies {
classpath(enforcedPlatform("org.springframework.boot:spring-boot-dependencies:2.2.5.RELEASE"))
}
}
Refer to the docs for more info:
https://docs.gradle.org/current/userguide/resolution_rules.html
https://docs.gradle.org/current/userguide/platforms.html

How do I stop gradle from upgrading transitive dependencies?

Why is Gradle changing my library's transitive dependency to a newer version? How do I get it to stop?
Details
I am working on an internal plugin library for my company that uses Spring Security. The plugin explicitly declares a dependency on the latest version of Spring Security 4:
compile ('org.springframework.security:spring-security-core:4.2.13.RELEASE') {
force = true
}
When I include the plugin in a client project, Gradle is upgrading me from spring security 4 to 5, which breaks the plugin.
compile 'com.mycompany:my-security-plugin:0.3.0-SNAPSHOT'
Here is the output from dependencyInsight in the client project:
> Task :dependencyInsight
org.springframework.security:spring-security-core:5.1.6.RELEASE (selected by rule)
variant "compile" [
org.gradle.status = release (not requested)
org.gradle.usage = java-api
org.gradle.component.category = library (not requested)
]
org.springframework.security:spring-security-core:5.1.6.RELEASE
+--- org.springframework.security:spring-security-config:5.1.6.RELEASE
| \--- com.mycompany:my-security-plugin:0.3.0-SNAPSHOT:20200122.162056-4 (requested org.springframework.security:spring-security-config:4.2.13.RELEASE)
| \--- compileClasspath
\--- org.springframework.security:spring-security-web:5.1.6.RELEASE
\--- com.mycompany:my-security-plugin:0.3.0-SNAPSHOT:20200122.162056-4 (requested org.springframework.security:spring-security-web:4.2.13.RELEASE) (*)
org.springframework.security:spring-security-core:4.2.13.RELEASE -> 5.1.6.RELEASE
\--- com.mycompany:my-security-plugin:0.3.0-SNAPSHOT:20200122.162056-4
\--- compileClasspath
It looks to me like in all cases, I am requesting spring security 4 in my config. What am I doing wrong?
I am using Gradle 5.1.1.
Update
As a workaround, it is possible to have the client app declare a direct dependency on spring security, using a specific version. I'm trying to avoid this, if possible.
Update 2
Output from gradlew dependencyInsight --dependency org.springframework.security:spring-security-web:
> Task :dependencyInsight
org.springframework.security:spring-security-web:5.1.6.RELEASE (selected by rule)
variant "compile" [
org.gradle.status = release (not requested)
org.gradle.usage = java-api
org.gradle.component.category = library (not requested)
]
org.springframework.security:spring-security-web:4.2.13.RELEASE -> 5.1.6.RELEASE
\--- com.mycompany:my-security-plugin:0.3.0-SNAPSHOT:20200122.162056-4
\--- compileClasspath
Update 3
The buildEnvironment includes the following, via grails:
+--- org.springframework.boot:spring-boot-gradle-plugin:2.1.9.RELEASE
| | +--- org.springframework.boot:spring-boot-loader-tools:2.1.9.RELEASE (*)
| | +--- io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE
Am I correct in assuming that you use the Spring Dependency Management Plugin (io.spring.dependency-management) and maybe also the Spring Boot Gradle Plugin (org.springframework.boot)? Then it’s very likely that this combination will also manage the eventual version of spring-security-core for you, irrespective of what other pieces of your build setup request.
Minimal Setup Demonstrating the Problem
I have the following two Gradle builds sitting next to each other (omitting the Gradle 5.1.1 Wrapper files for brevity):
my-security-plugin
└── build.gradle
my-client
├── build.gradle
└── settings.gradle
Running the following Gradle command from within my-client gives me (roughly) the same output as in the OP’s question:
./gradlew dependencyInsight --configuration compile --dependency org.springframework.security:spring-security-core
my-security-plugin/build.gradle
plugins {
id 'java'
}
group = 'com.mycompany'
version = '0.3.0-SNAPSHOT'
repositories {
jcenter()
}
dependencies {
compile ('org.springframework.security:spring-security-core:4.2.13.RELEASE') {
force = true
}
compile 'org.springframework.security:spring-security-config:4.2.13.RELEASE'
compile 'org.springframework.security:spring-security-web:4.2.13.RELEASE'
}
my-client/build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}
group = 'com.mycompany'
version = '1.0.0'
repositories {
jcenter()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
compile 'com.mycompany:my-security-plugin:0.3.0-SNAPSHOT'
}
my-client/settings.gradle
includeBuild '../my-security-plugin'
This file is only here to define a Gradle composite build; includeBuild won’t be there in the production build, you will rather publish the my-security-plugin to some repository from where my-client downloads it.
Possible Solutions to the Problem
As mentioned in the introduction, the combination of Spring Dependency Management Plugin and Spring Boot Gradle Plugin defines the version of spring-security-core for your build. Aside from your own workaround, there are three possibilities to get to the version you want:
Use the Spring Boot version that selects the same version of spring-security-core that you need: id 'org.springframework.boot' version '1.5.22.RELEASE'
Customize the managed versions by setting an extra property in your build: ext['spring-security.version'] = '4.2.13.RELEASE'
Don’t let Spring manage your dependencies at all …
All these solutions obviously have drawbacks:
solution 1 forces you to use an old Spring Boot version
solution 2 forces you to request the required version of a transitive dependency in your client app (which is similar to your own workaround that you wanted to avoid)
solution 3 may not be worth the extra work that manually managing all Spring dependencies would incur
Now you only have to choose the lesser evil ;-)

How do I force a gradle dependency for a specific sub-project?

This is what it is doing:
org.apache.santuario:xmlsec:1.5.7 -> 2.0.2
Just for this sub-project, I want to force 1.5.7. BTW this thing is buried pretty deep. There has to be a better way than peeling the onion to get at the one dependency for an exclude. I mean if I have a dependency X why can't I programatically cascade down the tree doing the exclude? This is Gradle, right?
This almost worked:
configurations {
compile.resolutionStrategy {
force 'org.apache.santuario:xmlsec:1.5.7'
}
...
}
The above shows up correctly in the tree but 2.0.2 still winds up in the War.
Regardless anyway that is effective and doesn't require digging it out manualy, I'll take.
Instead of using force you might consider using substitute.
dependencies {
compile 'org.apache.santuario:xmlsec:2.0.1'
}
configurations.all {
resolutionStrategy {
// add dependency substitution rules
dependencySubstitution {
substitute module('org.apache.santuario:xmlsec') with module('org.apache.santuario:xmlsec:1.5.7')
}
}
}
Then I get an output of:
$ ./gradlew -q dependencies --configuration compile
------------------------------------------------------------
Root project
------------------------------------------------------------
compile - Dependencies for source set 'main'.
\--- org.apache.santuario:xmlsec:2.0.1 -> 1.5.7
\--- commons-logging:commons-logging:1.1.1

Resources