From 9fc3c7d9c602d1e85ca5094f0fca44cb115d549c Mon Sep 17 00:00:00 2001 From: James Nord Date: Wed, 5 Jun 2024 12:35:01 +0100 Subject: [PATCH 1/6] Dynamically create the RealJenkinsRuleInit plugin Switch to dynamically creating the plugin used by RealJenkinsRule The plugin will have its Jenkis baseline set to the version of the war to ensure that no detached plugins are pulled in. Previously if the version of Jenkins used by RealJenkinsRuleInit has detached plugins in the version of Jenknis used for the test then this can uncessescarily pull in detached plugins. In most cases this is harmless, but in some cases these plugins may be prevented from running under the test (e.g. testing FIPS and one of the detached plugins like eddsa is not FIPS complaint and blocks startup). The plugin itself has limited exposure to the Jenkins api (via hudson.plugin.Plugin and RealJenkinsRule$Init2). Code running from the plugin tests inside RealJenkinsRule useis the uberClassloader and if the plugin was targetting a lower core (where the dependencies have been deteached, they should still be included as this change only affects the version of the RealJenkinsRuleInit plugin. --- RealJenkinsRuleInit/pom.xml | 62 -------------- pom.xml | 1 - .../org/jvnet/hudson/test/PluginUtils.java | 81 ++++++++++++++++++ .../jvnet/hudson/test/RealJenkinsRule.java | 8 +- .../hudson/test/RealJenkinsRuleInit.java | 0 .../jvnet/hudson/test/RealJenkinsRuleInit.jpi | Bin 5311 -> 0 bytes .../jvnet/hudson/test/PluginUtilsTest.java | 26 ++++++ .../hudson/test/RealJenkinsRuleTest.java | 15 ++++ 8 files changed, 129 insertions(+), 64 deletions(-) delete mode 100644 RealJenkinsRuleInit/pom.xml create mode 100644 src/main/java/org/jvnet/hudson/test/PluginUtils.java rename {RealJenkinsRuleInit/src => src}/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java (100%) delete mode 100644 src/main/resources/org/jvnet/hudson/test/RealJenkinsRuleInit.jpi create mode 100644 src/test/java/org/jvnet/hudson/test/PluginUtilsTest.java diff --git a/RealJenkinsRuleInit/pom.xml b/RealJenkinsRuleInit/pom.xml deleted file mode 100644 index 2559202a3..000000000 --- a/RealJenkinsRuleInit/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - 4.0.0 - - org.jenkins-ci.plugins - plugin - 4.83 - - - io.jenkins.plugins - RealJenkinsRuleInit - 0-SNAPSHOT - hpi - - 2.361 - true - true - - RealJenkinsRule initialization wrapper - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.1.2 - - - copy-installed - install - - copy - - - - - ${project.groupId} - ${project.artifactId} - ${project.version} - ${project.packaging} - ${project.basedir}/../src/main/resources/org/jvnet/hudson/test - RealJenkinsRuleInit.jpi - - - - - - - - - - - repo.jenkins-ci.org - https://repo.jenkins-ci.org/public/ - - - - - repo.jenkins-ci.org - https://repo.jenkins-ci.org/public/ - - - diff --git a/pom.xml b/pom.xml index 09ef7f2f8..1842784cc 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,6 @@ THE SOFTWARE. 999999-SNAPSHOT - 2.361 1.37 jenkinsci/${project.artifactId} diff --git a/src/main/java/org/jvnet/hudson/test/PluginUtils.java b/src/main/java/org/jvnet/hudson/test/PluginUtils.java new file mode 100644 index 000000000..59c13d6ab --- /dev/null +++ b/src/main/java/org/jvnet/hudson/test/PluginUtils.java @@ -0,0 +1,81 @@ +package org.jvnet.hudson.test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +class PluginUtils { + + /** + * Creates the plugin used by RealJenkinsRule + * @param destinationDirectory directory to write the plugin to. + * @param baseline the version of Jenkins to target + * @throws IOException if something goes wrong whilst creating the plugin. + * @return File the plugin we just created + */ + @SuppressFBWarnings(value="PATH_TRAVERSAL_IN", justification = "jth is a test utility, this is package scope code") + static File createRealJenkinsRulePlugin(File destinationDirectory, String baseline) throws IOException { + final String pluginName = RealJenkinsRuleInit.class.getSimpleName(); + + // The manifest is reused in the plugin and the classes jar. + Manifest mf = new Manifest(); + Attributes mainAttributes = mf.getMainAttributes(); + mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + mainAttributes.putValue("Plugin-Class", RealJenkinsRuleInit.class.getName()); + mainAttributes.putValue("Extension-Name", pluginName); + mainAttributes.putValue("Short-Name", pluginName); + mainAttributes.putValue("Long-Name", "RealJenkinsRule initialization wrapper"); + mainAttributes.putValue("Plugin-Version", "0-SNAPSHOT (private rjr)"); + mainAttributes.putValue("Support-Dynamic-Loading", "true"); + mainAttributes.putValue("Jenkins-Version", baseline); + + // we need to create a jar for the classes which we can then put into the plugin. + Path tmpClassesJar = Files.createTempFile("rjr", "jar"); + try (FileOutputStream fos = new FileOutputStream(tmpClassesJar.toFile()); + JarOutputStream classesJarOS = new JarOutputStream(fos, mf)) { + + // the actual class + try (InputStream classIS = RealJenkinsRuleInit.class.getResourceAsStream(RealJenkinsRuleInit.class.getSimpleName() + ".class")) { + String path = RealJenkinsRuleInit.class.getPackageName().replace('.', '/'); + createJarEntry(classesJarOS, path + '/' + RealJenkinsRuleInit.class.getSimpleName() + ".class", classIS); + } + createJarEntry(classesJarOS, "META-INF/services/hudson.Plugin", RealJenkinsRuleInit.class.getName().getBytes(StandardCharsets.UTF_8)); + } + + // the actual JPI + File jpi = new File(destinationDirectory, pluginName+".jpi"); + try (FileOutputStream fos = new FileOutputStream(jpi); JarOutputStream jos = new JarOutputStream(fos, mf)) { + try (FileInputStream fis = new FileInputStream(tmpClassesJar.toFile())) { + createJarEntry(jos, "WEB-INF/lib/"+pluginName+".jar", fis); + } + } + Files.delete(tmpClassesJar); + return jpi; + } + + private static void createJarEntry(JarOutputStream jos, String entryName, InputStream data) throws IOException { + JarEntry je = new JarEntry(entryName); + jos.putNextEntry(je); + data.transferTo(jos); + jos.closeEntry(); + } + + private static void createJarEntry(JarOutputStream jos, String entryName, byte[] data) throws IOException { + JarEntry je = new JarEntry(entryName); + jos.putNextEntry(je); + jos.write(data); + jos.closeEntry(); + } + +} diff --git a/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java index 65aa6278f..76a035b4c 100644 --- a/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java +++ b/src/main/java/org/jvnet/hudson/test/RealJenkinsRule.java @@ -75,6 +75,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.Manifest; import java.util.logging.ConsoleHandler; @@ -512,9 +513,14 @@ private void provision() throws Exception { if (localData != null) { new HudsonHomeLoader.Local(description.getTestClass().getMethod(description.getMethodName()), localData.value()).copy(getHome()); } + File plugins = new File(getHome(), "plugins"); Files.createDirectories(plugins.toPath()); - FileUtils.copyURLToFile(RealJenkinsRule.class.getResource("RealJenkinsRuleInit.jpi"), new File(plugins, "RealJenkinsRuleInit.jpi")); + try (JarFile jf = new JarFile(war)) { + // set the version to the version of jenkins used for testing to avoid dragging in detached plugins + String targetJenkinsVersion = jf.getManifest().getMainAttributes().getValue("Jenkins-Version"); + PluginUtils.createRealJenkinsRulePlugin(plugins, targetJenkinsVersion); + } if (includeTestClasspathPlugins) { // Adapted from UnitTestSupportingPluginManager & JenkinsRule.recipeLoadCurrentPlugin: diff --git a/RealJenkinsRuleInit/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java similarity index 100% rename from RealJenkinsRuleInit/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java rename to src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java diff --git a/src/main/resources/org/jvnet/hudson/test/RealJenkinsRuleInit.jpi b/src/main/resources/org/jvnet/hudson/test/RealJenkinsRuleInit.jpi deleted file mode 100644 index 7b8a1e62634c237c0bcd8816023580c973ae2cc3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5311 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 plugins = r.jenkins.getPluginManager().getPlugins(); + assertThat(plugins, hasSize(1)); + assertThat(plugins.get(0).getShortName(), is("RealJenkinsRuleInit")); + } } From 43926b76ae0059023ac77aea698329269a5b0c7a Mon Sep 17 00:00:00 2001 From: James Nord Date: Fri, 7 Jun 2024 11:09:00 +0100 Subject: [PATCH 2/6] Leave comment about use of this and prevent external usage Name the classloader whilst we are here :) --- .../org/jvnet/hudson/test/RealJenkinsRuleInit.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java index a0ffd80bb..bda48f1cf 100644 --- a/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java +++ b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java @@ -27,8 +27,19 @@ import hudson.Plugin; import java.net.URL; import java.net.URLClassLoader; + +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; + import jenkins.model.Jenkins; +/** + * Plugin for use by {@link RealJenkinsRule}. + *

+ * NOTE: this and only this class is added into a dynamically generated plugin, see {@link PluginUtils#createRealJenkinsRulePlugin(java.io.File, String)}. + * In order for this to occur correctly there need to be no inner classes or other code dependencies here (except what can be loaded by reflection). + */ +@Restricted(NoExternalUse.class) public class RealJenkinsRuleInit extends Plugin { @SuppressWarnings("deprecation") // @Initializer just gets run too late, even with before = InitMilestone.PLUGINS_PREPARED @@ -36,7 +47,7 @@ public RealJenkinsRuleInit() {} @Override public void start() throws Exception { - new URLClassLoader(new URL[] {new URL(System.getProperty("RealJenkinsRule.location"))}, ClassLoader.getSystemClassLoader().getParent()). + new URLClassLoader("RealJenkinsRule",new URL[] {new URL(System.getProperty("RealJenkinsRule.location"))}, ClassLoader.getSystemClassLoader().getParent()). loadClass("org.jvnet.hudson.test.RealJenkinsRule$Init2"). getMethod("run", Object.class). invoke(null, Jenkins.get()); From 65f7a2d3f75a5e1372cc5a388a47115a7e308771 Mon Sep 17 00:00:00 2001 From: James Nord Date: Fri, 7 Jun 2024 11:10:01 +0100 Subject: [PATCH 3/6] update comment to ensure it is correct --- src/test/java/org/jvnet/hudson/test/RealJenkinsRuleTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleTest.java b/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleTest.java index a6a4d96d3..d4ee20649 100644 --- a/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleTest.java +++ b/src/test/java/org/jvnet/hudson/test/RealJenkinsRuleTest.java @@ -361,7 +361,7 @@ private static void hangs(JenkinsRule r) throws Throwable { @Test public void noDetachedPlugins() throws Throwable { - // XXX this test will falsely pass if the RealJenkinsRuleInit plugin targets a version of Jenkins that has no detacthed plugins. + // we should be the only plugin in Jenkins. rr.then(RealJenkinsRuleTest::_noDetachedPlugins); } From ffd3c1ea2ed8fdab4651e7592d51af926ae229fd Mon Sep 17 00:00:00 2001 From: James Nord Date: Fri, 7 Jun 2024 11:13:43 +0100 Subject: [PATCH 4/6] skip creating the services and delete the tmp file in a finally --- .../org/jvnet/hudson/test/PluginUtils.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jvnet/hudson/test/PluginUtils.java b/src/main/java/org/jvnet/hudson/test/PluginUtils.java index 59c13d6ab..de6e128a7 100644 --- a/src/main/java/org/jvnet/hudson/test/PluginUtils.java +++ b/src/main/java/org/jvnet/hudson/test/PluginUtils.java @@ -42,26 +42,27 @@ static File createRealJenkinsRulePlugin(File destinationDirectory, String baseli // we need to create a jar for the classes which we can then put into the plugin. Path tmpClassesJar = Files.createTempFile("rjr", "jar"); - try (FileOutputStream fos = new FileOutputStream(tmpClassesJar.toFile()); - JarOutputStream classesJarOS = new JarOutputStream(fos, mf)) { - - // the actual class - try (InputStream classIS = RealJenkinsRuleInit.class.getResourceAsStream(RealJenkinsRuleInit.class.getSimpleName() + ".class")) { - String path = RealJenkinsRuleInit.class.getPackageName().replace('.', '/'); - createJarEntry(classesJarOS, path + '/' + RealJenkinsRuleInit.class.getSimpleName() + ".class", classIS); + try { + try (FileOutputStream fos = new FileOutputStream(tmpClassesJar.toFile()); + JarOutputStream classesJarOS = new JarOutputStream(fos, mf)) { + // the actual class + try (InputStream classIS = RealJenkinsRuleInit.class.getResourceAsStream(RealJenkinsRuleInit.class.getSimpleName() + ".class")) { + String path = RealJenkinsRuleInit.class.getPackageName().replace('.', '/'); + createJarEntry(classesJarOS, path + '/' + RealJenkinsRuleInit.class.getSimpleName() + ".class", classIS); + } } - createJarEntry(classesJarOS, "META-INF/services/hudson.Plugin", RealJenkinsRuleInit.class.getName().getBytes(StandardCharsets.UTF_8)); - } - // the actual JPI - File jpi = new File(destinationDirectory, pluginName+".jpi"); - try (FileOutputStream fos = new FileOutputStream(jpi); JarOutputStream jos = new JarOutputStream(fos, mf)) { - try (FileInputStream fis = new FileInputStream(tmpClassesJar.toFile())) { - createJarEntry(jos, "WEB-INF/lib/"+pluginName+".jar", fis); + // the actual JPI + File jpi = new File(destinationDirectory, pluginName+".jpi"); + try (FileOutputStream fos = new FileOutputStream(jpi); JarOutputStream jos = new JarOutputStream(fos, mf)) { + try (FileInputStream fis = new FileInputStream(tmpClassesJar.toFile())) { + createJarEntry(jos, "WEB-INF/lib/" + pluginName + ".jar", fis); + } } + return jpi; + } finally { + Files.delete(tmpClassesJar); } - Files.delete(tmpClassesJar); - return jpi; } private static void createJarEntry(JarOutputStream jos, String entryName, InputStream data) throws IOException { @@ -71,11 +72,4 @@ private static void createJarEntry(JarOutputStream jos, String entryName, InputS jos.closeEntry(); } - private static void createJarEntry(JarOutputStream jos, String entryName, byte[] data) throws IOException { - JarEntry je = new JarEntry(entryName); - jos.putNextEntry(je); - jos.write(data); - jos.closeEntry(); - } - } From 91ecb5e6f597d7883ae50243cbe8a4467167cbad Mon Sep 17 00:00:00 2001 From: James Nord Date: Fri, 7 Jun 2024 14:12:13 +0100 Subject: [PATCH 5/6] Update src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java Co-authored-by: Jesse Glick --- src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java index bda48f1cf..9644bdb60 100644 --- a/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java +++ b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java @@ -36,7 +36,7 @@ /** * Plugin for use by {@link RealJenkinsRule}. *

- * NOTE: this and only this class is added into a dynamically generated plugin, see {@link PluginUtils#createRealJenkinsRulePlugin(java.io.File, String)}. + * NOTE: this and only this class is added into a dynamically generated plugin, see {@link PluginUtils#createRealJenkinsRulePlugin(java.io.File, String)}. * In order for this to occur correctly there need to be no inner classes or other code dependencies here (except what can be loaded by reflection). */ @Restricted(NoExternalUse.class) From defc0f73e753897698cb9fe3dac086b4461f7fa8 Mon Sep 17 00:00:00 2001 From: James Nord Date: Fri, 7 Jun 2024 16:24:48 +0100 Subject: [PATCH 6/6] leave a warning in the comment --- src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java index 9644bdb60..dd357488d 100644 --- a/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java +++ b/src/main/java/org/jvnet/hudson/test/RealJenkinsRuleInit.java @@ -34,7 +34,7 @@ import jenkins.model.Jenkins; /** - * Plugin for use by {@link RealJenkinsRule}. + * Plugin for use internally only by {@link RealJenkinsRule}, do not use this from plugin test code! *

* NOTE: this and only this class is added into a dynamically generated plugin, see {@link PluginUtils#createRealJenkinsRulePlugin(java.io.File, String)}. * In order for this to occur correctly there need to be no inner classes or other code dependencies here (except what can be loaded by reflection).