[MNG-8176] Restrict classloader for Maven 4 plugins (#1336)

This commit is contained in:
Guillaume Nodet 2024-08-12 23:57:01 +02:00 committed by GitHub
parent 9c1871fd22
commit 2a709dc038
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 6 deletions

View File

@ -48,6 +48,13 @@ public interface ClassRealmManager {
*/ */
ClassRealm getMavenApiRealm(); 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. * Creates a new class realm for the specified project and its build extensions.
* *

View File

@ -26,12 +26,14 @@ import java.io.File;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.stream.Collectors;
import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.classrealm.ClassRealmRequest.RealmType; import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
@ -57,6 +59,8 @@ import org.slf4j.LoggerFactory;
public class DefaultClassRealmManager implements ClassRealmManager { public class DefaultClassRealmManager implements ClassRealmManager {
public static final String API_REALMID = "maven.api"; 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 * 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. * 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 mavenApiRealm;
private final ClassRealm maven4ApiRealm;
/** /**
* Patterns of artifacts provided by maven core and exported via maven api realm. These artifacts are filtered from * 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. * plugin and build extensions realms to avoid presence of duplicate and possibly conflicting classes on classpath.
*/ */
private final Set<String> providedArtifacts; private final Set<String> providedArtifacts;
private final Set<String> providedArtifactsV4;
@Inject @Inject
public DefaultClassRealmManager( public DefaultClassRealmManager(
CoreRealm coreRealm, List<ClassRealmManagerDelegate> delegates, CoreExports exports) { CoreRealm coreRealm, List<ClassRealmManagerDelegate> delegates, CoreExports exports) {
@ -101,7 +109,16 @@ public class DefaultClassRealmManager implements ClassRealmManager {
foreignImports, foreignImports,
null /* artifacts */); null /* artifacts */);
Map<String, ClassLoader> 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.providedArtifacts = exports.getExportedArtifacts();
this.providedArtifactsV4 = providedArtifacts.stream()
.filter(ga -> ga.startsWith("org.apache.maven:maven-api-"))
.collect(Collectors.toSet());
} }
private ClassRealm newRealm(String id) { private ClassRealm newRealm(String id) {
@ -128,6 +145,11 @@ public class DefaultClassRealmManager implements ClassRealmManager {
return mavenApiRealm; return mavenApiRealm;
} }
@Override
public ClassRealm getMaven4ApiRealm() {
return maven4ApiRealm;
}
/** /**
* Creates a new class realm with the specified parent and imports. * Creates a new class realm with the specified parent and imports.
* *
@ -150,8 +172,9 @@ public class DefaultClassRealmManager implements ClassRealmManager {
List<ClassRealmConstituent> constituents = new ArrayList<>(artifacts == null ? 0 : artifacts.size()); List<ClassRealmConstituent> constituents = new ArrayList<>(artifacts == null ? 0 : artifacts.size());
if (artifacts != null && !artifacts.isEmpty()) { if (artifacts != null && !artifacts.isEmpty()) {
boolean v4api = foreignImports != null && foreignImports.containsValue(maven4ApiRealm);
for (Artifact artifact : artifacts) { for (Artifact artifact : artifacts) {
if (!isProvidedArtifact(artifact) && artifact.getFile() != null) { if (!isProvidedArtifact(artifact, v4api) && artifact.getFile() != null) {
constituents.add(new ArtifactClassRealmConstituent(artifact)); constituents.add(new ArtifactClassRealmConstituent(artifact));
} else if (logger.isDebugEnabled()) { } else if (logger.isDebugEnabled()) {
logger.debug(" Excluded: {}", getId(artifact)); logger.debug(" Excluded: {}", getId(artifact));
@ -211,8 +234,9 @@ public class DefaultClassRealmManager implements ClassRealmManager {
getKey(plugin, true), RealmType.Extension, PARENT_CLASSLOADER, null, foreignImports, artifacts); getKey(plugin, true), RealmType.Extension, PARENT_CLASSLOADER, null, foreignImports, artifacts);
} }
private boolean isProvidedArtifact(Artifact artifact) { private boolean isProvidedArtifact(Artifact artifact, boolean v4api) {
return providedArtifacts.contains(artifact.getGroupId() + ":" + artifact.getArtifactId()); Set<String> provided = v4api ? providedArtifactsV4 : providedArtifacts;
return provided.contains(artifact.getGroupId() + ":" + artifact.getArtifactId());
} }
public ClassRealm createPluginRealm( public ClassRealm createPluginRealm(

View File

@ -363,7 +363,8 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
pluginDescriptor.setClassRealm(pluginRealm); pluginDescriptor.setClassRealm(pluginRealm);
pluginDescriptor.setArtifacts(pluginArtifacts); pluginDescriptor.setArtifacts(pluginArtifacts);
} else { } else {
Map<String, ClassLoader> foreignImports = calcImports(project, parent, imports); boolean v4api = pluginDescriptor.getMojos().stream().anyMatch(MojoDescriptor::isV4Api);
Map<String, ClassLoader> foreignImports = calcImports(project, parent, imports, v4api);
PluginRealmCache.Key cacheKey = pluginRealmCache.createKey( PluginRealmCache.Key cacheKey = pluginRealmCache.createKey(
plugin, plugin,
@ -471,14 +472,16 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
return Collections.unmodifiableList(artifacts); return Collections.unmodifiableList(artifacts);
} }
private Map<String, ClassLoader> calcImports(MavenProject project, ClassLoader parent, List<String> imports) { private Map<String, ClassLoader> calcImports(
MavenProject project, ClassLoader parent, List<String> imports, boolean v4api) {
Map<String, ClassLoader> foreignImports = new HashMap<>(); Map<String, ClassLoader> foreignImports = new HashMap<>();
ClassLoader projectRealm = project.getClassRealm(); ClassLoader projectRealm = project.getClassRealm();
if (projectRealm != null) { if (projectRealm != null) {
foreignImports.put("", projectRealm); foreignImports.put("", projectRealm);
} else { } else {
foreignImports.put("", classRealmManager.getMavenApiRealm()); foreignImports.put(
"", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm());
} }
if (parent != null && imports != null) { if (parent != null && imports != null) {