From 5c749cfac3c64693c4e71cbfc519340d421b1d7f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 4 Dec 2017 23:11:31 -0500 Subject: [PATCH] Support dynamically-loaded plugins in CustomClassFilter.Contributed. --- core/src/main/java/hudson/PluginManager.java | 4 ++++ .../jenkins/security/CustomClassFilter.java | 3 ++- .../jenkins/security/CustomClassFilterTest.java | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index c816805ba6..ab203c20ec 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -150,6 +150,7 @@ import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.SEVERE; import static java.util.logging.Level.WARNING; +import jenkins.security.CustomClassFilter; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -858,6 +859,9 @@ public abstract class PluginManager extends AbstractModelObject implements OnMas ((UberClassLoader) uberClassLoader).loaded.clear(); } + // TODO antimodular; perhaps should have a PluginListener to complement ExtensionListListener? + CustomClassFilter.Contributed.load(); + try { p.resolvePluginDependencies(); strategy.load(p); diff --git a/core/src/main/java/jenkins/security/CustomClassFilter.java b/core/src/main/java/jenkins/security/CustomClassFilter.java index e0cd7186aa..a417b82017 100644 --- a/core/src/main/java/jenkins/security/CustomClassFilter.java +++ b/core/src/main/java/jenkins/security/CustomClassFilter.java @@ -131,7 +131,7 @@ public interface CustomClassFilter extends ExtensionPoint { * !com.acme.illadvised.YoloReflectionFactory$Handle * */ - @Restricted(DoNotUse.class) + @Restricted(NoExternalUse.class) @Extension public class Contributed implements CustomClassFilter { @@ -154,6 +154,7 @@ public interface CustomClassFilter extends ExtensionPoint { @Initializer(after = InitMilestone.PLUGINS_PREPARED, before = InitMilestone.PLUGINS_STARTED, fatal = false) public static void load() throws IOException { Map overrides = ExtensionList.lookup(CustomClassFilter.class).get(Contributed.class).overrides; + overrides.clear(); Enumeration resources = Jenkins.getInstance().getPluginManager().uberClassLoader.getResources("META-INF/hudson.remoting.ClassFilter"); while (resources.hasMoreElements()) { try (InputStream is = resources.nextElement().openStream()) { diff --git a/test/src/test/java/jenkins/security/CustomClassFilterTest.java b/test/src/test/java/jenkins/security/CustomClassFilterTest.java index a8797f4ebc..72efda1500 100644 --- a/test/src/test/java/jenkins/security/CustomClassFilterTest.java +++ b/test/src/test/java/jenkins/security/CustomClassFilterTest.java @@ -25,6 +25,7 @@ package jenkins.security; import hudson.remoting.ClassFilter; +import java.io.File; import java.util.logging.Level; import javax.script.ScriptEngineManager; import javax.script.ScriptException; @@ -32,10 +33,12 @@ import javax.script.SimpleBindings; import jenkins.util.BuildListenerAdapter; import jenkins.util.TreeString; import jenkins.util.TreeStringBuilder; +import org.apache.commons.io.FileUtils; import org.junit.Test; import static org.hamcrest.Matchers.*; import org.junit.Rule; import org.junit.rules.ErrorCollector; +import org.junit.rules.TemporaryFolder; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.recipes.WithPlugin; @@ -55,6 +58,9 @@ public class CustomClassFilterTest { @Rule public LoggerRule logging = new LoggerRule().record("jenkins.security", Level.FINER); + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + @WithPlugin("custom-class-filter.jpi") @Test public void smokes() throws Exception { @@ -67,6 +73,17 @@ public class CustomClassFilterTest { assertBlacklisted("disabled via plugin", TreeStringBuilder.class, true); } + @Test + public void dynamicLoad() throws Exception { + assertBlacklisted("not yet enabled via plugin", ScriptException.class, true); + assertBlacklisted("not yet disabled via plugin", TreeStringBuilder.class, false); + File jpi = tmp.newFile("custom-class-filter.jpi"); + FileUtils.copyURLToFile(CustomClassFilterTest.class.getResource("/plugins/custom-class-filter.jpi"), jpi); + r.jenkins.pluginManager.dynamicLoad(jpi); + assertBlacklisted("enabled via plugin", ScriptException.class, false); + assertBlacklisted("disabled via plugin", TreeStringBuilder.class, true); + } + private void assertBlacklisted(String message, Class c, boolean blacklisted) { String name = c.getName(); errors.checkThat(name + ": " + message, ClassFilter.DEFAULT.isBlacklisted(c) || ClassFilter.DEFAULT.isBlacklisted(name), is(blacklisted));