From 2a709dc03896d6b06a29819d1b969b408827916c Mon Sep 17 00:00:00 2001 From: Guillaume Nodet Date: Mon, 12 Aug 2024 23:57:01 +0200 Subject: [PATCH] [MNG-8176] Restrict classloader for Maven 4 plugins (#1336) --- .../maven/classrealm/ClassRealmManager.java | 7 +++++ .../classrealm/DefaultClassRealmManager.java | 30 +++++++++++++++++-- .../internal/DefaultMavenPluginManager.java | 9 ++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java b/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java index b0386f040ba..689aa620031 100644 --- a/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java +++ b/maven-core/src/main/java/org/apache/maven/classrealm/ClassRealmManager.java @@ -48,6 +48,13 @@ public interface ClassRealmManager { */ ClassRealm getMavenApiRealm(); + /** + * Gets the class realm exposing the Maven 4 API. This is basically a restricted view on the Maven core realm. + * + * @return The class realm exposing the Maven API, never {@code null}. + */ + ClassRealm getMaven4ApiRealm(); + /** * Creates a new class realm for the specified project and its build extensions. * diff --git a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 14bec0c1c04..77276934014 100644 --- a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -26,12 +26,14 @@ import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.TreeMap; +import java.util.stream.Collectors; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.classrealm.ClassRealmRequest.RealmType; @@ -57,6 +59,8 @@ public class DefaultClassRealmManager implements ClassRealmManager { public static final String API_REALMID = "maven.api"; + public static final String API_V4_REALMID = "maven.api.v4"; + /** * During normal command line build, ClassWorld is loaded by jvm system classloader, which only includes * plexus-classworlds jar and possibly javaagent classes, see https://issues.apache.org/jira/browse/MNG-4747. @@ -78,12 +82,16 @@ public class DefaultClassRealmManager implements ClassRealmManager { private final ClassRealm mavenApiRealm; + private final ClassRealm maven4ApiRealm; + /** * Patterns of artifacts provided by maven core and exported via maven api realm. These artifacts are filtered from * plugin and build extensions realms to avoid presence of duplicate and possibly conflicting classes on classpath. */ private final Set providedArtifacts; + private final Set providedArtifactsV4; + @Inject public DefaultClassRealmManager( CoreRealm coreRealm, List delegates, CoreExports exports) { @@ -101,7 +109,16 @@ public DefaultClassRealmManager( foreignImports, null /* artifacts */); + Map apiV4Imports = new HashMap<>(); + apiV4Imports.put("org.apache.maven.api", containerRealm); + apiV4Imports.put("org.slf4j", containerRealm); + this.maven4ApiRealm = createRealm(API_V4_REALMID, RealmType.Core, null, null, apiV4Imports, null); + this.providedArtifacts = exports.getExportedArtifacts(); + + this.providedArtifactsV4 = providedArtifacts.stream() + .filter(ga -> ga.startsWith("org.apache.maven:maven-api-")) + .collect(Collectors.toSet()); } private ClassRealm newRealm(String id) { @@ -128,6 +145,11 @@ public ClassRealm getMavenApiRealm() { return mavenApiRealm; } + @Override + public ClassRealm getMaven4ApiRealm() { + return maven4ApiRealm; + } + /** * Creates a new class realm with the specified parent and imports. * @@ -150,8 +172,9 @@ private ClassRealm createRealm( List constituents = new ArrayList<>(artifacts == null ? 0 : artifacts.size()); if (artifacts != null && !artifacts.isEmpty()) { + boolean v4api = foreignImports != null && foreignImports.containsValue(maven4ApiRealm); for (Artifact artifact : artifacts) { - if (!isProvidedArtifact(artifact) && artifact.getFile() != null) { + if (!isProvidedArtifact(artifact, v4api) && artifact.getFile() != null) { constituents.add(new ArtifactClassRealmConstituent(artifact)); } else if (logger.isDebugEnabled()) { logger.debug(" Excluded: {}", getId(artifact)); @@ -211,8 +234,9 @@ public ClassRealm createExtensionRealm(Plugin plugin, List artifacts) getKey(plugin, true), RealmType.Extension, PARENT_CLASSLOADER, null, foreignImports, artifacts); } - private boolean isProvidedArtifact(Artifact artifact) { - return providedArtifacts.contains(artifact.getGroupId() + ":" + artifact.getArtifactId()); + private boolean isProvidedArtifact(Artifact artifact, boolean v4api) { + Set provided = v4api ? providedArtifactsV4 : providedArtifacts; + return provided.contains(artifact.getGroupId() + ":" + artifact.getArtifactId()); } public ClassRealm createPluginRealm( diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java index 908f608cd02..77d11a27068 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java @@ -363,7 +363,8 @@ public void setupPluginRealm( pluginDescriptor.setClassRealm(pluginRealm); pluginDescriptor.setArtifacts(pluginArtifacts); } else { - Map foreignImports = calcImports(project, parent, imports); + boolean v4api = pluginDescriptor.getMojos().stream().anyMatch(MojoDescriptor::isV4Api); + Map foreignImports = calcImports(project, parent, imports, v4api); PluginRealmCache.Key cacheKey = pluginRealmCache.createKey( plugin, @@ -471,14 +472,16 @@ private List toMavenArtifacts(DependencyResult dependencyResult) { return Collections.unmodifiableList(artifacts); } - private Map calcImports(MavenProject project, ClassLoader parent, List imports) { + private Map calcImports( + MavenProject project, ClassLoader parent, List imports, boolean v4api) { Map foreignImports = new HashMap<>(); ClassLoader projectRealm = project.getClassRealm(); if (projectRealm != null) { foreignImports.put("", projectRealm); } else { - foreignImports.put("", classRealmManager.getMavenApiRealm()); + foreignImports.put( + "", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm()); } if (parent != null && imports != null) {