-
Notifications
You must be signed in to change notification settings - Fork 0
/
000_log4shell_mitigation.init.gradle
110 lines (99 loc) · 5.2 KB
/
000_log4shell_mitigation.init.gradle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Authors:
//
// Hugh Greene <[email protected]>
// Jenn Archibald <[email protected]>
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Files
// Remove any JndiManager and JndiLookup class files found in log4j-core jars
// for 2.x versions before 2.16.0 during dependency resolution, as a mitigation for
// CVE-2021-44228 and CVE-2021-45102.
// See <https://logging.apache.org/log4j/2.x/security.html>.
//
// This allows you to build older versions of existing projects without updating
// and avoid the log4shell vulnerability. You may wish to add other files to
// the log4ShellFilesToDelete list based on various pieces of advice on the
// Internet, e.g., <https://snyk.io/blog/log4shell-remediation-cheat-sheet/>.
//
// WARNING: To make sure this takes effect, you should remove all existing versions
// of org.apache.logging.log4j/log4j-core from your Gradle cache (typically under
// $GRADLE_USER_HOME/caches/modules-2/files-2.1/).
//
// Requires JDK 1.7 and Gradle 3.4.
gradle.addListener(new DependencyResolutionListener() {
public static final List<String> LOG4SHELL_FILES_TO_DELETE = [
'org/apache/logging/log4j/core/lookup/JndiLookup.class',
'org/apache/logging/log4j/core/net/JndiManager.class',
]
private final Object deleteLock = new Object()
@Override
void beforeResolve(final ResolvableDependencies dependencies) { }
@Override
void afterResolve(final ResolvableDependencies dependencies) {
// We use a lenient ArtifactView in case we are being asked to resolve a lenient Configuration.
Collection<ArtifactResult> log4JCoreArtifacts =
dependencies.artifactView { lenient = true }.artifacts.findAll {
ComponentIdentifier id = it.id.componentIdentifier
(id instanceof ModuleComponentIdentifier) && isApplicableVersion(id)
}
log4JCoreArtifacts
.findAll { ArtifactResult it ->
it instanceof ResolvedArtifactResult && it.file.name.endsWith('.jar')
}
.forEach { ResolvedArtifactResult it -> deleteLog4ShellFiles(dependencies.path, it.file) }
}
/**
* Versions of log4j-core 2.x before 2.16 benefit from having two JNDI-related classes removed; from 2.16 onwards
* those classes are fixed and, moreover, removing them may cause log4j projects to fail to run, with a {@link
* java.lang.NoClassDefFoundError}.
*/
boolean isApplicableVersion(ModuleComponentIdentifier id) {
final isLog4JCore = id.group == 'org.apache.logging.log4j' && id.module == 'log4j-core'
if (!isLog4JCore) {
// Return early to skip version number parsing for efficiency.
return false
}
// We don't need general-purpose version parsing as per the complex Gradle algorithm (see
// https://docs.gradle.org/current/userguide/single_versions.html); we only need to parse log4j versions in the
// 2.x line, to check if they're earlier than 2.16. These are of the form "2.Y.Z", "2.Y", or "2.0-P" for
// numbers Y & Z, where P is something like "rc1" or "beta9", which we ignore.
final String[] parts = id.version.split("[.-]")
if (parts[0] != '2') {
return false
}
if (parts.length == 1) {
logger.warn("Ignoring log4j-core with unexpected version number format: '${id.version}'")
return false
}
try {
return (Integer.parseInt(parts[1]) < 16)
} catch (NumberFormatException ignored) {
logger.warn("Ignoring log4j-core with unexpected version number format: '${id.version}'")
return false
}
}
void deleteLog4ShellFiles(String resolvableDependenciesPath, File file) {
// Multiple configurations with the same dependencies could be resolved in parallel, so lock JAR file access.
synchronized (deleteLock) {
getZipFileSystem(file).withCloseable { FileSystem zipFileSystem ->
deleteLog4ShellFiles(resolvableDependenciesPath, file, zipFileSystem)
}
}
}
FileSystem getZipFileSystem(File file) {
return FileSystems.newFileSystem(URI.create("jar:${file.toURI()}"), ['create': 'false'])
}
void deleteLog4ShellFiles(String resolvableDependenciesPath, File file, FileSystem zipFileSystem) {
LOG4SHELL_FILES_TO_DELETE.forEach { String fileToDelete ->
if (Files.deleteIfExists(zipFileSystem.getPath(fileToDelete))) {
logger.warn("While resolving ${resolvableDependenciesPath}, " +
"in order to avoid log4shell vulnerabilities, deleted ${fileToDelete} from '${file}'. " +
"Consider upgrading your project to use a version of log4j without the vulnerability.")
} else {
logger.debug("While resolving ${resolvableDependenciesPath}, " +
"and deleting files to avoid log4shell vulnerabilities, " +
"did not find ${fileToDelete} in '${file}'.")
}
}
}
})