diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 218b9532b268..aef9bce64eff 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,8 +24,6 @@ updates: - dependency-name: "jakarta.servlet.jsp.jstl.jakarta.servlet.jsp.jstl-api" # see https://github.com/jenkinsci/jenkins/pull/4224 can't be updated without breaking api - dependency-name: "org.jfree:jfreechart" - # needs guava upgrades first https://github.com/jenkinsci/jenkins/pull/5200#pullrequestreview-579741619 - - dependency-name: "com.google.inject:guice" # the dependency is actually provided by the Web container, hence it is aligned with Jetty. See https://github.com/jenkinsci/jenkins/pull/5211 - dependency-name: "javax.servlet:javax.servlet-api" # log4j 1.2.17 is the final 1.x release diff --git a/bom/pom.xml b/bom/pom.xml index ed78c1c28508..6d31d0cab12f 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -96,7 +96,7 @@ THE SOFTWARE. com.google.inject guice - 4.0 + 5.0.1 @@ -176,12 +176,12 @@ THE SOFTWARE. org.jenkins-ci version-number - 1.7 + 1.8 org.jenkins-ci crypto-util - 1.5 + 1.6 org.connectbot.jbcrypt @@ -206,7 +206,7 @@ THE SOFTWARE. org.jenkins-ci task-reactor - 1.5 + 1.6 org.jvnet.localizer @@ -256,12 +256,12 @@ THE SOFTWARE. org.jenkins-ci memory-monitor - 1.9 + 1.10 net.java.dev.jna jna - 5.9.0 + 5.10.0 org.kohsuke diff --git a/core/src/main/java/hudson/ClassicPluginStrategy.java b/core/src/main/java/hudson/ClassicPluginStrategy.java index 7f62a9e244e2..a7f3259bb525 100644 --- a/core/src/main/java/hudson/ClassicPluginStrategy.java +++ b/core/src/main/java/hudson/ClassicPluginStrategy.java @@ -641,9 +641,7 @@ protected Class findClass(String name) throws ClassNotFoundException { if (PluginManager.FAST_LOOKUP) { for (PluginWrapper pw : getTransitiveDependencies()) { try { - Class c = ClassLoaderReflectionToolkit._findLoadedClass(pw.classLoader, name); - if (c!=null) return c; - return ClassLoaderReflectionToolkit._findClass(pw.classLoader, name); + return ClassLoaderReflectionToolkit.loadClass(pw.classLoader, name); } catch (ClassNotFoundException ignored) { //not found. try next } diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java index 47bb91c8ed14..1d780278df5b 100644 --- a/core/src/main/java/hudson/PluginManager.java +++ b/core/src/main/java/hudson/PluginManager.java @@ -2197,15 +2197,7 @@ protected Class findClass(String name) throws ClassNotFoundException { if (FAST_LOOKUP) { for (PluginWrapper p : activePlugins) { try { - Class c = ClassLoaderReflectionToolkit._findLoadedClass(p.classLoader, name); - if (c != null) { - synchronized (loaded) { - loaded.put(name, c); - } - return c; - } - // calling findClass twice appears to cause LinkageError: duplicate class def - c = ClassLoaderReflectionToolkit._findClass(p.classLoader, name); + Class c = ClassLoaderReflectionToolkit.loadClass(p.classLoader, name); synchronized (loaded) { loaded.put(name, c); } diff --git a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java index 42b013452249..edce6b431a00 100644 --- a/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java +++ b/core/src/main/java/jenkins/ClassLoaderReflectionToolkit.java @@ -70,6 +70,7 @@ private static class GetClassLoadingLock { /** * Calls {@link ClassLoader#findLoadedClass} while holding {@link ClassLoader#getClassLoadingLock}. * @since 1.553 + * @deprecated use {@link #loadClass(ClassLoader, String)} */ public static @CheckForNull Class _findLoadedClass(ClassLoader cl, String name) { synchronized (getClassLoadingLock(cl, name)) { @@ -100,6 +101,7 @@ private static class FindLoadedClass { /** * Calls {@link ClassLoader#findClass} while holding {@link ClassLoader#getClassLoadingLock}. * @since 1.553 + * @deprecated use {@link #loadClass(ClassLoader, String)} */ public static @NonNull Class _findClass(ClassLoader cl, String name) throws ClassNotFoundException { synchronized (getClassLoadingLock(cl, name)) { @@ -124,6 +126,48 @@ private static class FindClass { } } + /** + * Load the class with the specified binary name. This method searches for classes in the + * following order: + * + *
    + *
  1. + *

    Invoke {@link ClassLoader#findLoadedClass(String)} to check if the class has already + * been loaded. + *

  2. + *

    Invoke the {@link ClassLoader#findClass(String)} method to find the class. + *

+ * + *

This method synchronizes on the result of {@link ClassLoader#getClassLoadingLock(String)} + * during the entire class loading process. + * + * @param cl The {@link ClassLoader} to use. + * @param name The binary name of the class. + * @return The resulting {@link Class} object. + * @throws ClassNotFoundException If the class could not be found. + * @since 2.TODO + */ + public static @NonNull Class loadClass(ClassLoader cl, String name) throws ClassNotFoundException { + synchronized (getClassLoadingLock(cl, name)) { + // First, check if the class has already been loaded. + Class c; + if (cl instanceof JenkinsClassLoader) { + c = ((JenkinsClassLoader) cl).findLoadedClass2(name); + } else { + c = (Class) invoke(FindLoadedClass.FIND_LOADED_CLASS, RuntimeException.class, cl, name); + } + if (c != null) { + return c; + } + + // Find the class. + if (cl instanceof JenkinsClassLoader) { + return ((JenkinsClassLoader) cl).findClass(name); + } else { + return (Class) invoke(FindClass.FIND_CLASS, ClassNotFoundException.class, cl, name); + } + } + } /** * Calls {@link ClassLoader#findResource}. diff --git a/core/src/main/java/jenkins/ProxyInjector.java b/core/src/main/java/jenkins/ProxyInjector.java index 3736b32b08aa..fad1d24680ae 100644 --- a/core/src/main/java/jenkins/ProxyInjector.java +++ b/core/src/main/java/jenkins/ProxyInjector.java @@ -31,6 +31,8 @@ import com.google.inject.Provider; import com.google.inject.Scope; import com.google.inject.TypeLiteral; +import com.google.inject.spi.Element; +import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.TypeConverterBinding; import java.lang.annotation.Annotation; import java.util.List; @@ -134,4 +136,14 @@ public Map, Scope> getScopeBindings() { public Set getTypeConverterBindings() { return resolve().getTypeConverterBindings(); } + + @Override + public List getElements() { + return resolve().getElements(); + } + + @Override + public Map, List> getAllMembersInjectorInjectionPoints() { + return resolve().getAllMembersInjectorInjectionPoints(); + } } diff --git a/core/src/main/java/jenkins/model/InterruptedBuildAction.java b/core/src/main/java/jenkins/model/InterruptedBuildAction.java index 9bcfefc17a8c..f67088a54044 100644 --- a/core/src/main/java/jenkins/model/InterruptedBuildAction.java +++ b/core/src/main/java/jenkins/model/InterruptedBuildAction.java @@ -43,11 +43,11 @@ public class InterruptedBuildAction extends InvisibleAction { private final List causes; public InterruptedBuildAction(Collection causes) { - this.causes = Collections.unmodifiableList(new ArrayList<>(causes)); + this.causes = new ArrayList<>(causes); } @Exported public List getCauses() { - return causes; + return Collections.unmodifiableList(causes); } } diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index 86a5d4030715..6a46ec37db3e 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -2239,14 +2239,38 @@ public DescribableList, NodePropertyDescriptor> getGlobalNodePro * but we also call this periodically to self-heal any data out-of-sync issue. */ /*package*/ void trimLabels() { + trimLabels((Set) null); + } + + /** + * Reset labels and remove invalid ones for the given nodes. + * @param nodes the nodes taken as reference to update labels + */ + void trimLabels(Node... nodes) { + Set includedLabels = new HashSet<>(); + Arrays.asList(nodes).stream().filter(Objects::nonNull).forEach(n -> includedLabels.addAll(n.getAssignedLabels())); + trimLabels(includedLabels); + } + + /** + * Reset labels and remove invalid ones for the given nodes. + * @param includedLabels the labels taken as reference to update labels. If {@code null}, all labels are considered. + */ + private void trimLabels(@CheckForNull Set includedLabels) { Set

diff --git a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly index 40ee03ea2aaf..edac188c6267 100644 --- a/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly +++ b/core/src/main/resources/hudson/widgets/HistoryWidget/index.jelly @@ -79,16 +79,9 @@ THE SOFTWARE. - -
- No builds -
-
- - - +
+ ${%No builds} +
diff --git a/core/src/main/resources/jenkins/model/SimpleGlobalBuildDiscarderStrategy/config.properties b/core/src/main/resources/jenkins/model/SimpleGlobalBuildDiscarderStrategy/config.properties index 621323e43ad5..7a7d5ebe939d 100644 --- a/core/src/main/resources/jenkins/model/SimpleGlobalBuildDiscarderStrategy/config.properties +++ b/core/src/main/resources/jenkins/model/SimpleGlobalBuildDiscarderStrategy/config.properties @@ -1 +1 @@ -blurb = The selected build discarder with be applied after any build finishes, as well as periodically. +blurb = The selected build discarder will be applied after any build finishes, as well as periodically. diff --git a/core/src/main/resources/jenkins/security/whitelisted-classes.txt b/core/src/main/resources/jenkins/security/whitelisted-classes.txt index ad997f303e53..b7d404940628 100644 --- a/core/src/main/resources/jenkins/security/whitelisted-classes.txt +++ b/core/src/main/resources/jenkins/security/whitelisted-classes.txt @@ -1,12 +1,15 @@ com.google.common.collect.AbstractListMultimap +com.google.common.collect.AbstractMapBasedMultimap com.google.common.collect.AbstractMultimap com.google.common.collect.AbstractSetMultimap # Artifactory - https://issues.jenkins.io/browse/JENKINS-48991 com.google.common.collect.ArrayListMultimap +com.google.common.collect.ArrayListMultimapGwtSerializationDependencies com.google.common.collect.EmptyImmutableList com.google.common.collect.EmptyImmutableSet com.google.common.collect.EmptyImmutableSortedSet com.google.common.collect.HashMultimap +com.google.common.collect.HashMultimapGwtSerializationDependencies # First hit: https://github.com/jenkinsci/pitmutation-plugin/ com.google.common.collect.HashMultiset com.google.common.collect.ImmutableList diff --git a/test/src/test/java/jenkins/security/Jenkins67105Test.java b/test/src/test/java/jenkins/security/Jenkins67105Test.java new file mode 100644 index 000000000000..0a39a3d1c6b5 --- /dev/null +++ b/test/src/test/java/jenkins/security/Jenkins67105Test.java @@ -0,0 +1,69 @@ +package jenkins.security; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.BuildListener; +import hudson.model.FreeStyleProject; +import hudson.tasks.Builder; +import java.io.IOException; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.Issue; +import org.jvnet.hudson.test.JenkinsRule; + +public class Jenkins67105Test { + + @Rule public JenkinsRule r = new JenkinsRule(); + + @Issue("JENKINS-67105") + @Test + public void arrayListMultimap() throws Exception { + FreeStyleProject p = r.createFreeStyleProject(); + p.setAssignedNode(r.createSlave()); + p.getBuildersList().add(new GuavaBuilder(new ArrayListMultimapCallable())); + r.buildAndAssertSuccess(p); + } + + @Issue("JENKINS-67105") + @Test + public void hashMultimap() throws Exception { + FreeStyleProject p = r.createFreeStyleProject(); + p.setAssignedNode(r.createSlave()); + p.getBuildersList().add(new GuavaBuilder(new HashMultimapCallable())); + r.buildAndAssertSuccess(p); + } + + public static class GuavaBuilder extends Builder { + private final MasterToSlaveCallable callable; + + public GuavaBuilder(MasterToSlaveCallable callable) { + this.callable = callable; + } + + @Override + public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) + throws InterruptedException, IOException { + listener.getLogger().println("received " + launcher.getChannel().call(callable)); + return true; + } + } + + private static class ArrayListMultimapCallable + extends MasterToSlaveCallable, RuntimeException> { + @Override + public Multimap call() throws RuntimeException { + return ArrayListMultimap.create(); + } + } + + private static class HashMultimapCallable + extends MasterToSlaveCallable, RuntimeException> { + @Override + public Multimap call() throws RuntimeException { + return HashMultimap.create(); + } + } +} diff --git a/war/src/main/js/filter-build-history.js b/war/src/main/js/filter-build-history.js index 70b8677edd5b..a6662439b3df 100644 --- a/war/src/main/js/filter-build-history.js +++ b/war/src/main/js/filter-build-history.js @@ -1,20 +1,20 @@ import debounce from 'lodash/debounce' const buildHistoryContainer = document.getElementById("buildHistory") -const pageSearchInputContainer = document.getElementsBySelector('#buildHistory .build-search-row .jenkins-search')[0] -const pageSearchInput = document.getElementsBySelector('#buildHistory .build-search-row input')[0] +const pageSearchInputContainer = buildHistoryContainer.querySelector('.build-search-row .jenkins-search') +const pageSearchInput = buildHistoryContainer.querySelector('.build-search-row input') const buildHistoryPage = document.getElementById("buildHistoryPage") const properties = document.getElementById("properties") const ajaxUrl = buildHistoryPage.getAttribute("page-ajax") const nextBuild = properties.getAttribute("page-next-build") const noBuildsBanner = document.getElementById("no-builds") -const sidePanel = $('side-panel'); -const buildHistoryPageNav = $('buildHistoryPageNav'); +const sidePanel = document.getElementById('side-panel'); +const buildHistoryPageNav = document.getElementById('buildHistoryPageNav'); -const pageOne = Element.getElementsBySelector(buildHistoryPageNav, '.pageOne')[0]; -const pageUp = Element.getElementsBySelector(buildHistoryPageNav, '.pageUp')[0]; -const pageDown = Element.getElementsBySelector(buildHistoryPageNav, '.pageDown')[0]; +const pageOne = buildHistoryPageNav.querySelectorAll('.pageOne')[0]; +const pageUp = buildHistoryPageNav.querySelectorAll('.pageUp')[0]; +const pageDown = buildHistoryPageNav.querySelectorAll('.pageDown')[0]; const leftRightPadding = 4; const updateBuildsRefreshInterval = 5000; @@ -27,12 +27,19 @@ function updateBuilds() { var dataTable = getDataTable(buildHistoryContainer); var rows = dataTable.rows; + // Check there are no existing rows (except the search bar) before showing the no builds banner + if (rows.length <= 1 && rsp.responseText === "
") { + noBuildsBanner.style.display = "block"; + } else { + noBuildsBanner.style.display = "none"; + } + //delete rows with transitive data var firstBuildRow = 0; if (rows[firstBuildRow].classList.contains('build-search-row')) { firstBuildRow++; } - while (rows.length > 0 && rows[firstBuildRow].classList.contains('transitive')) { + while (rows.length > 1 && rows[firstBuildRow].classList.contains('transitive')) { Element.remove(rows[firstBuildRow]); } @@ -51,7 +58,7 @@ function updateBuilds() { } else { // The data table has no rows. In this case, we just add all new rows directly to the // table, one after the other i.e. we don't insert before a "pivot" row (first row). - dataTable.appendChild(newRows[0]); + dataTable.getElementsByTagName("tbody")[0].appendChild(newRows[0]); } } diff --git a/war/src/main/less/modules/side-panel-widgets.less b/war/src/main/less/modules/side-panel-widgets.less index 353cbb13fc69..4dbff43c616d 100644 --- a/war/src/main/less/modules/side-panel-widgets.less +++ b/war/src/main/less/modules/side-panel-widgets.less @@ -225,10 +225,10 @@ &__information { text-align: center; line-height: 80px; - background-color: rgba(204, 204, 204, 0.2); + background-color: var(--panel-header-bg-color); margin-top: 10px; font-weight: 600; - border-radius: 6px; + border-radius: var(--form-input-border-radius); } .build-row {