Hi!
The rush to patch log4j2 in plugins got me thinking if we can do something to reduce the risks of vulnerabilities from dependencies in plugins.
The log4j2 dependency in particular seems like one that shouldn't have been there at all.
Back in the days of J2EE Application servers, the recommendation was - use a provided dependency for the APIs of things like logging, LDAP, persistence, etc. when building an application. The server will provide the implementation of those APIs.
When developing a library or framework, the recommendation historically was to use an API for logging - like commons-logging, slf4j-api. This would let the consumer of the library decide on the implementation. Typically, this meant the user would configure a CLI or a webserver like tomcat with a logging implementation - log4j (v1), log4j-core (v2), or logback.
With apps that run as jars - like jenkins, that distinction is a little harder to make. I personally, run jenkins within tomcat, and am familiar with configuring logging at the tomcat level.
I wrote this tool - https://github.com/rahulsom/analyze-jenkins-plugins. Right now it can generate a CSV listing plugins and the jars that they bundle. It does as much as possible to clearly identify the jars going in. When the jar contains one (or more) pom.properties it lists those out. For jars that do not contain that, it uses the SHA1 to identify the jar. That means it can sufficiently identify jars when they get shadowed or renamed. There are still cases where it fails, but those failures are few enough. This is an example of the CSV it generates - https://gist.github.com/rahulsom/4f992c5c16afc91ee4113ec5da61c21b
This is all libraries bundling log4j-core. It's the same libraries bundling log4j-api. One of them is shadow-jar-ing.
❯ csvsql "SELECT pluginName, pluginVersion, jarName, jarGroupId, jarArtifactId, jarVersion, jarGAVSource
FROM plugin-jars.csv
WHERE jarArtifactId = 'log4j-core'"
pluginName pluginVersion jarName jarGroupId jarArtifactId jarVersion jarGAVSource
audit-log 1.2 WEB-INF/lib/log4j-core-2.13.3.jar org.apache.logging.log4j log4j-core 2.13.3 POM
bootstraped-multi-test-results-report 2.1.3 WEB-INF/lib/log4j-core-2.6.2.jar org.apache.logging.log4j log4j-core 2.6.2 POM
checkmarx 2021.4.2 WEB-INF/lib/log4j-core-2.13.3.jar org.apache.logging.log4j log4j-core 2.13.3 POM
hp-application-automation-tools-plugin 7.1 WEB-INF/lib/log4j-core-2.13.3.jar org.apache.logging.log4j log4j-core 2.13.3 POM
lambdatest-automation 1.19.4 WEB-INF/lib/log4j-core-2.11.2.jar org.apache.logging.log4j log4j-core 2.11.2 POM
peass-ci 2.0.0-540.v244012ecda48 WEB-INF/lib/log4j-core-2.15.0.jar org.apache.logging.log4j log4j-core 2.15.0 POM
pipeline-huaweicloud-plugin 0.0.1 WEB-INF/lib/log4j-core-2.8.2.jar org.apache.logging.log4j log4j-core 2.8.2 POM
reliza-integration 0.1.13 WEB-INF/lib/log4j-core-2.14.0.jar org.apache.logging.log4j log4j-core 2.14.0 POM
talend 1.3-rc42.f3ec422d618b WEB-INF/lib/log4j-core-2.15.0.jar org.apache.logging.log4j log4j-core 2.15.0 POM
testdroid-run-in-cloud 2.116.0 WEB-INF/lib/log4j-core-2.13.3.jar org.apache.logging.log4j log4j-core 2.13.3 POM
thundra-foresight 11.vbc9483778bb3 WEB-INF/lib/thundra-agent-maven-test-instrumentation-0.0.6.jar org.apache.logging.log4j log4j-core 2.14.1 POM
thundra-foresight 11.vbc9483778bb3 WEB-INF/lib/log4j-core-2.14.1.jar org.apache.logging.log4j log4j-core 2.14.1 POM
venafi-vcert 2.0.0 WEB-INF/lib/log4j-core-2.13.3.jar org.apache.logging.log4j log4j-core 2.13.3 POM
xray-connector 2.5.1 WEB-INF/lib/log4j-core-2.13.3.jar org.apache.logging.log4j log4j-core 2.13.3 POM
This is the first 20 libraries bundling slf4j-api.
❯ csvsql "SELECT pluginName, pluginVersion, jarName, jarGroupId, jarArtifactId, jarVersion, jarGAVSource
FROM plugin-jars.csv
WHERE jarArtifactId = 'slf4j-api'
LIMIT 20"
pluginName pluginVersion jarName jarGroupId jarArtifactId jarVersion jarGAVSource
GatekeeperPlugin 3.0.5 WEB-INF/lib/slf4j-api-1.6.4.jar org.slf4j slf4j-api 1.6.4 POM
NegotiateSSO 1.4 WEB-INF/lib/slf4j-api-2.0.0-alpha0.jar org.slf4j slf4j-api 2.0.0-alpha0 POM
acunetix-360-scan 2.0.1 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
adobe-cloud-manager 43.v4560e7fa191c WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
analysis-model-api 10.8.1 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
appcenter 0.11.1 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
aqua-security-scanner 3.1.2 WEB-INF/lib/slf4j-api-1.7.26.jar org.slf4j slf4j-api 1.7.26 POM
artifactory 3.14.2 WEB-INF/lib/slf4j-api-1.7.25.jar org.slf4j slf4j-api 1.7.25 POM
atlassian-jira-software-cloud 1.4.4 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
autocomplete-parameter 1.1 WEB-INF/lib/slf4j-api-1.7.31.jar org.slf4j slf4j-api 1.7.31 POM
autograding 3.3.1 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
aws-kinesis-consumer 1.0.5 WEB-INF/lib/slf4j-api-1.7.32.jar org.slf4j slf4j-api 1.7.32 POM
aws-lambda 0.5.10 WEB-INF/lib/slf4j-api-1.7.10.jar org.slf4j slf4j-api 1.7.10 POM
aws-yum-parameter 1.5 WEB-INF/lib/slf4j-api-1.7.7.jar org.slf4j slf4j-api 1.7.7 POM
azure-credentials-ext 1.0 WEB-INF/lib/slf4j-api-1.7.26.jar org.slf4j slf4j-api 1.7.26 POM
bitbucket-kubernetes-credentials 0.0.4 WEB-INF/lib/slf4j-api-1.7.26.jar org.slf4j slf4j-api 1.7.26 POM
blackduck-detect 7.0.0 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
build-failure-analyzer 2.1.0 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
build-time-blame 64.vd8f4018a2bbe WEB-INF/lib/slf4j-api-1.7.26.jar org.slf4j slf4j-api 1.7.26 POM
ca-mat-performance-benchmarking-by-broadcom 1.5 WEB-INF/lib/slf4j-api-1.7.30.jar org.slf4j slf4j-api 1.7.30 POM
Looking at jenkins-core's POM - https://mvnrepository.com/artifact/org.jenkins-ci.main/jenkins-core/2.324 it looks like it has a top level dependency on these two libraries
org.slf4j:jcl-over-slf4j:1.7.32
org.slf4j:log4j-over-slf4j:1.7.32
They in turn have a dependency on slf4j-api
org.slf4j:slf4j-api:1.7.32
That suggests that Jenkins Core would prefer plugins to use slf4j-api. If that's the case, all plugins that depend on jenkins-core should use slf4j-api and remove the dependency on any other logging library.
Is my analysis of the plugins reasonable?
If it is, we have a few points to influence plugins:
- The parent-pom used by plugins uses enforcer to block some libraries being in the dependency tree. We could add most logging libraries to that list.
- The maven-hpi-plugin could do some linting of the plugins to ensure they only use slf4j-api and even then, only get it as a transitive from the provided dependency on jenkins-core.
- I could send PRs to the plugins that bundle logging libraries.
I think there is more analysis we can do for other problems - there are a lot of plugins bundling okhttp jars instead of depending on the okhttp-api plugin. Plugins could lose a lot of weight by cleaning up their builds to bundle only libraries that are unique to their needs. I suspect that would also improve jenkins startup time for users who depend on a lot of these plugins.
Thanks!
Rahul