diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 8c2bd4291a6e..000000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,68 +0,0 @@
----
-version: 2
-updates:
- - package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "daily"
- - package-ecosystem: "maven"
- directory: "/"
- schedule:
- interval: "daily"
- ignore:
- # Exclusions in this section have been triaged and determined to be
- # permanent. We do not anticipate removing exclusions from this section.
-
- # Provided by Jetty and should be aligned with the version provided by the
- # version of Jetty we deliver. See:
- # https://github.com/jenkinsci/jenkins/pull/5211
- - dependency-name: "jakarta.servlet:jakarta.servlet-api"
-
- # Jetty Maven Plugin and Winstone should be upgraded in lockstep in order
- # to keep their corresponding Jetty versions aligned.
- - dependency-name: "org.eclipse.jetty:jetty-maven-plugin"
- - dependency-name: "org.jenkins-ci:winstone"
-
- # Here lies technical debt. Exclusions in this section have been triaged
- # and determined to be temporary. Exclusions should be removed from this
- # section once the remaining action items have been completed.
-
- # Contains incompatible API changes and needs compatibility work.
- - dependency-name: "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api"
-
- # Needs significant testing. See:
- # https://github.com/jenkinsci/jenkins/pull/5112#issuecomment-744429487
- # https://github.com/jenkinsci/jenkins/pull/5116#issuecomment-744526638
- - dependency-name: "org.codehaus.groovy:groovy-all"
- versions: [">=2.5.0"]
-
- # Consumed by Groovy and should be updated in lockstep with Groovy. See:
- # https://github.com/jenkinsci/jenkins/pull/5184
- - dependency-name: "org.fusesource.jansi:jansi"
-
- # Contains incompatible API changes and needs compatibility work. See:
- # https://github.com/jenkinsci/jenkins/pull/4224
- - dependency-name: "org.jfree:jfreechart"
-
- # Starting with 6.x, Spring requires Java 17 at a minimum.
- - dependency-name: "org.springframework:spring-framework-bom"
- versions: [">=6.0.0"]
-
- # Starting with 6.x, Spring Security requires Java 17 at a minimum.
- - dependency-name: "org.springframework.security:spring-security-bom"
- versions: [">=6.0.0"]
-
- # Starting with 7.x, Guice switches from javax.* to jakarta.* bindings.
- # See https://github.com/google/guice/wiki/Guice700
- - dependency-name: "com.google.inject:guice-bom"
- versions: [">=7.0.0"]
- - package-ecosystem: "maven"
- directory: "/"
- target-branch: "stable-2.452"
- labels:
- - "into-lts"
- - "needs-justification"
- schedule:
- interval: "daily"
- # Include only security updates and exclude version updates.
- open-pull-requests-limit: 0
diff --git a/.github/renovate.json b/.github/renovate.json
index 8c3c4ad17cea..aab994ad8187 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json
@@ -1,63 +1,221 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
- "config:base",
+ "config:recommended",
":disableDependencyDashboard",
":semanticCommitsDisabled"
],
- "enabledManagers": ["npm", "regex"],
- "postUpdateOptions": ["yarnDedupeHighest"],
+ "prHourlyLimit": 0,
+ "prConcurrentLimit": 0,
+ "postUpdateOptions": [
+ "yarnDedupeHighest"
+ ],
"packageRules": [
{
- "matchDatasources": ["npm"],
- "addLabels": ["javascript"],
- "stabilityDays": 3,
- "reviewers": ["team:sig-ux"]
+ "matchDatasources": [
+ "npm"
+ ],
+ "addLabels": [
+ "javascript"
+ ],
+ "minimumReleaseAge": "3 days",
+ "reviewers": [
+ "team:sig-ux"
+ ]
},
{
- "matchPackageNames": ["node"],
+ "matchPackageNames": [
+ "node"
+ ],
"allowedVersions": "/20.[0-9]+.[0-9]+(.[0-9]+)?$/"
+ },
+ {
+ "description": "Should be upgraded in lockstep in order to keep their corresponding Jetty versions aligned, could be grouped but releases are likely separated by a bit of time",
+ "matchManagers": [
+ "maven"
+ ],
+ "enabled": false,
+ "matchPackageNames": [
+ "org.eclipse.jetty:jetty-maven-plugin",
+ "org.jenkins-ci:winstone"
+ ]
+ },
+ {
+ "description": "Provided by Jetty and should be aligned with the version provided by the version of Jetty we deliver. See: https://github.com/jenkinsci/jenkins/pull/5211",
+ "matchManagers": [
+ "maven"
+ ],
+ "enabled": false,
+ "matchPackageNames": [
+ "jakarta.servlet:jakarta.servlet-api",
+ "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api"
+ ]
+ },
+ {
+ "description": "Needs significant testing. See: https://github.com/jenkinsci/jenkins/pull/5112#issuecomment-744429487 and https://github.com/jenkinsci/jenkins/pull/5116#issuecomment-744526638",
+ "matchManagers": [
+ "maven"
+ ],
+ "allowedVersions": "<2.5.0",
+ "matchPackageNames": [
+ "org.codehaus.groovy:groovy-all"
+ ]
+ },
+ {
+ "description": "Consumed by Groovy and should be updated in lockstep with Groovy. See: https://github.com/jenkinsci/jenkins/pull/5184",
+ "matchManagers": [
+ "maven"
+ ],
+ "enabled": false,
+ "matchPackageNames": [
+ "org.fusesource.jansi:jansi"
+ ]
+ },
+ {
+ "description": "Depends on commons-lang3 which is in progress for removal from core. See: https://issues.jenkins.io/browse/JENKINS-73355",
+ "matchManagers": [
+ "maven"
+ ],
+ "enabled": false,
+ "matchPackageNames": [
+ "org.apache.commons:commons-compress"
+ ]
+ },
+ {
+ "description": "Contains incompatible API changes and needs compatibility work. See: https://github.com/jenkinsci/jenkins/pull/4224",
+ "matchManagers": [
+ "maven"
+ ],
+ "enabled": false,
+ "matchPackageNames": [
+ "org.jfree:jfreechart"
+ ]
+ },
+ {
+ "description": "Starting with 6.x, Spring requires Java 17 at a minimum.",
+ "matchManagers": [
+ "maven"
+ ],
+ "allowedVersions": "<6.0.0",
+ "matchPackageNames": [
+ "org.springframework:spring-framework-bom",
+ "org.springframework.security:spring-security-bom"
+ ]
+ },
+ {
+ "description": "Starting with 7.x, Guice switches from javax.* to jakarta.* bindings. See https://github.com/google/guice/wiki/Guice700",
+ "matchManagers": [
+ "maven"
+ ],
+ "allowedVersions": "<7.0.0",
+ "matchPackageNames": [
+ "com.google.inject:guice-bom"
+ ]
+ },
+ {
+ "matchFileNames": [
+ "core/pom.xml",
+ "test/pom.xml",
+ "war/pom.xml"
+ ],
+ "matchPackageNames": [
+ "org.jenkins-ci.main:remoting"
+ ],
+ "description": "Avoid updating the remoting.minimum.supported.version property but still update latest one by not placing this property in the parent pom.xml",
+ "enabled": false
+ },
+ {
+ "matchPackageNames": [
+ "net.jcip:jcip-annotations"
+ ],
+ "matchDatasources": [
+ "maven"
+ ],
+ "enabled": false,
+ "description": "maven-metadata.xml is missing for this really old package which is required by renovate"
}
],
- "regexManagers": [
+ "customManagers": [
{
- "fileMatch": ["war/pom.xml"],
- "matchStrings": ["(?.*?)"],
+ "customType": "regex",
+ "fileMatch": [
+ "war/pom.xml"
+ ],
+ "matchStrings": [
+ "(?.*?)"
+ ],
"depNameTemplate": "node",
"datasourceTemplate": "npm"
},
{
- "fileMatch": ["ath.sh"],
- "matchStrings": ["export ATH_VERSION=(?.*?)\n"],
+ "customType": "regex",
+ "fileMatch": [
+ "ath.sh"
+ ],
+ "matchStrings": [
+ "export ATH_VERSION=(?.*?)\n"
+ ],
"depNameTemplate": "jenkins/ath",
"datasourceTemplate": "docker",
"versioningTemplate": "loose"
},
{
- "fileMatch": [".gitpod/Dockerfile"],
- "matchStrings": ["ARG MAVEN_VERSION=(?.*?)\n"],
+ "customType": "regex",
+ "fileMatch": [
+ ".gitpod/Dockerfile"
+ ],
+ "matchStrings": [
+ "ARG MAVEN_VERSION=(?.*?)\n"
+ ],
"depNameTemplate": "org.apache.maven:maven-core",
"datasourceTemplate": "maven"
},
{
- "fileMatch": ["core/src/site/site.xml"],
- "matchStrings": ["lit@(?.*?)/"],
+ "customType": "regex",
+ "fileMatch": [
+ "core/src/site/site.xml"
+ ],
+ "matchStrings": [
+ "lit@(?.*?)/"
+ ],
"depNameTemplate": "lit",
"datasourceTemplate": "npm"
},
{
- "fileMatch": ["core/src/site/site.xml"],
- "matchStrings": ["webcomponentsjs@(?.*?)/"],
+ "customType": "regex",
+ "fileMatch": [
+ "core/src/site/site.xml"
+ ],
+ "matchStrings": [
+ "webcomponentsjs@(?.*?)/"
+ ],
"depNameTemplate": "@webcomponents/webcomponentsjs",
"datasourceTemplate": "npm"
},
{
- "fileMatch": ["core/src/site/site.xml"],
- "matchStrings": ["(?.*?)<\/version>"],
+ "customType": "regex",
+ "fileMatch": [
+ "core/src/site/site.xml"
+ ],
+ "matchStrings": [
+ "(?.*?)"
+ ],
"depNameTemplate": "org.apache.maven.skins:maven-fluido-skin",
"datasourceTemplate": "maven"
}
],
- "labels": ["dependencies", "skip-changelog"],
- "rebaseWhen": "conflicted"
+ "labels": [
+ "dependencies",
+ "skip-changelog"
+ ],
+ "rebaseWhen": "conflicted",
+ "ignorePaths": [
+ "**/node_modules/**",
+ "**/bower_components/**",
+ "**/vendor/**",
+ "**/examples/**",
+ "**/__tests__/**",
+ "**/tests/**",
+ "**/__fixtures__/**"
+ ]
}
diff --git a/.github/workflows/publish-release-artifact.yml b/.github/workflows/publish-release-artifact.yml
index ae5e6bf92602..8b7242136ce9 100644
--- a/.github/workflows/publish-release-artifact.yml
+++ b/.github/workflows/publish-release-artifact.yml
@@ -73,7 +73,7 @@ jobs:
wget -q https://get.jenkins.io/${REPO}/${PROJECT_VERSION}/${FILE_NAME}
- name: Upload Release Asset
id: upload-war
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
+ uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -108,7 +108,7 @@ jobs:
- name: Upload Release Asset
id: upload-deb
if: always()
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
+ uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -144,7 +144,7 @@ jobs:
- name: Upload Release Asset
id: upload-rpm
if: always()
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
+ uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -180,7 +180,7 @@ jobs:
- name: Upload Release Asset
id: upload-msi
if: always()
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
+ uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -216,7 +216,7 @@ jobs:
- name: Upload Release Asset
id: upload-suse-rpm
if: always()
- uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191
+ uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index de5572116383..ca018ebc3ab9 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -32,6 +32,9 @@
+
+
+
diff --git a/ath.sh b/ath.sh
index 3cd7dfd86d75..eaa4396152f0 100644
--- a/ath.sh
+++ b/ath.sh
@@ -6,7 +6,7 @@ set -o xtrace
cd "$(dirname "$0")"
# https://github.com/jenkinsci/acceptance-test-harness/releases
-export ATH_VERSION=5911.v5f88b_6d0c450
+export ATH_VERSION=5929.v295db_5f04eb_1
if [[ $# -eq 0 ]]; then
export JDK=17
diff --git a/bom/pom.xml b/bom/pom.xml
index 8f8e6412f7b0..cf265f1dada9 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -39,8 +39,8 @@ THE SOFTWARE.
2.0.0-M2
- 2.0.13
- 1892.v73465f3d074d
+ 2.0.16
+ 1894.v82b_2fb_35519d2.4.21
@@ -72,7 +72,7 @@ THE SOFTWARE.
args4jargs4j
- 2.33
+ 2.37com.github.spotbugs
@@ -304,7 +304,7 @@ THE SOFTWARE.
org.jvnet.winpwinp
- 1.30
+ 1.31org.kohsuke
diff --git a/cli/pom.xml b/cli/pom.xml
index c037ed4a0ee8..6309811a2e8b 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -15,7 +15,7 @@
https://github.com/jenkinsci/jenkins
- 2.12.1
+ 2.13.2
@@ -65,7 +65,7 @@
org.glassfish.tyrus.bundlestyrus-standalone-client-jdk
- 2.1.5
+ 2.2.0true
diff --git a/core/pom.xml b/core/pom.xml
index 95c289164930..8e30ae211ed7 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -40,6 +40,8 @@ THE SOFTWARE.
2.10.0
+
+ 3107.v665000b_51092
diff --git a/core/src/main/java/hudson/PluginManager.java b/core/src/main/java/hudson/PluginManager.java
index 75dd52c4d7b7..16c22e9ca90c 100644
--- a/core/src/main/java/hudson/PluginManager.java
+++ b/core/src/main/java/hudson/PluginManager.java
@@ -2417,6 +2417,22 @@ public String toString() {
// only for debugging purpose
return "classLoader " + getClass().getName();
}
+
+ // TODO Remove this once we require post 2024-07 remoting minimum version and deleted ClassLoaderProxy#fetchJar(URL)
+ @SuppressFBWarnings(
+ value = "DMI_COLLECTION_OF_URLS",
+ justification = "All URLs point to local files, so no DNS lookup.")
+ @Restricted(NoExternalUse.class)
+ public boolean isPluginJar(URL jarUrl) {
+ for (PluginWrapper plugin : activePlugins) {
+ if (plugin.classLoader instanceof URLClassLoader) {
+ if (Set.of(((URLClassLoader) plugin.classLoader).getURLs()).contains(jarUrl)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
@SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
diff --git a/core/src/main/java/hudson/ProxyConfiguration.java b/core/src/main/java/hudson/ProxyConfiguration.java
index fa4206521ee6..cc5b30d956dc 100644
--- a/core/src/main/java/hudson/ProxyConfiguration.java
+++ b/core/src/main/java/hudson/ProxyConfiguration.java
@@ -540,6 +540,34 @@ public FormValidation doCheckPort(@QueryParameter String value) {
return FormValidation.ok();
}
+ /**
+ * Do check if the provided value is empty or composed of whitespaces.
+ * If so, return a validation warning.
+ *
+ * @param value the value to test
+ * @return a validation warning iff the provided value is empty or composed of whitespaces.
+ */
+ private static FormValidation checkProxyCredentials(String value) {
+ value = Util.fixEmptyAndTrim(value);
+ if (value == null) {
+ return FormValidation.ok();
+ } else {
+ return FormValidation.warning(Messages.ProxyConfiguration_NonTLSWarning());
+ }
+ }
+
+ @RequirePOST
+ @Restricted(NoExternalUse.class)
+ public FormValidation doCheckUserName(@QueryParameter String value) {
+ return checkProxyCredentials(value);
+ }
+
+ @RequirePOST
+ @Restricted(NoExternalUse.class)
+ public FormValidation doCheckSecretPassword(@QueryParameter String value) {
+ return checkProxyCredentials(value);
+ }
+
@RequirePOST
@Restricted(NoExternalUse.class)
public FormValidation doValidateProxy(
diff --git a/core/src/main/java/hudson/model/MyViewsProperty.java b/core/src/main/java/hudson/model/MyViewsProperty.java
index 71809d788eae..49fdfac48d2d 100644
--- a/core/src/main/java/hudson/model/MyViewsProperty.java
+++ b/core/src/main/java/hudson/model/MyViewsProperty.java
@@ -26,6 +26,7 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.Util;
import hudson.model.Descriptor.FormException;
@@ -42,6 +43,7 @@
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
+import jenkins.util.SystemProperties;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
@@ -51,6 +53,7 @@
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerFallback;
+import org.kohsuke.stapler.StaplerProxy;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.verb.POST;
@@ -60,7 +63,14 @@
*
* @author Tom Huybrechts
*/
-public class MyViewsProperty extends UserProperty implements ModifiableViewGroup, Action, StaplerFallback {
+public class MyViewsProperty extends UserProperty implements ModifiableViewGroup, Action, StaplerFallback, StaplerProxy {
+
+ /**
+ * Escape hatch for StaplerProxy-based access control
+ */
+ @Restricted(NoExternalUse.class)
+ @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "for script console")
+ public static /* non-final */ boolean SKIP_PERMISSION_CHECK = SystemProperties.getBoolean(MyViewsProperty.class.getName() + ".skipPermissionCheck");
/**
* Name of the primary view defined by the user.
@@ -226,7 +236,10 @@ public String getDisplayName() {
@Override
public String getIconFileName() {
- return "symbol-browsers";
+ if (SKIP_PERMISSION_CHECK || getACL().hasPermission(Jenkins.ADMINISTER))
+ return "symbol-browsers";
+ else
+ return null;
}
@Override
@@ -234,6 +247,14 @@ public String getUrlName() {
return "my-views";
}
+ @Override
+ public Object getTarget() {
+ if (!SKIP_PERMISSION_CHECK) {
+ checkPermission(Jenkins.ADMINISTER);
+ }
+ return this;
+ }
+
@Extension @Symbol("myView")
public static class DescriptorImpl extends UserPropertyDescriptor {
diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java
index a3376f99d75e..a648935527ae 100644
--- a/core/src/main/java/jenkins/model/Jenkins.java
+++ b/core/src/main/java/jenkins/model/Jenkins.java
@@ -183,6 +183,7 @@
import hudson.triggers.TriggerDescriptor;
import hudson.util.AdministrativeError;
import hudson.util.ClockDifference;
+import hudson.util.ComboBoxModel;
import hudson.util.CopyOnWriteList;
import hudson.util.CopyOnWriteMap;
import hudson.util.DaemonThreadFactory;
@@ -1908,6 +1909,11 @@ public Collection getJobNames() {
return names;
}
+ @Restricted(NoExternalUse.class)
+ public ComboBoxModel doFillJobNameItems() {
+ return new ComboBoxModel(getJobNames());
+ }
+
@Override
public List getViewActions() {
return getActions();
diff --git a/core/src/main/java/jenkins/security/s2m/JarURLValidatorImpl.java b/core/src/main/java/jenkins/security/s2m/JarURLValidatorImpl.java
new file mode 100644
index 000000000000..7fafcea946d5
--- /dev/null
+++ b/core/src/main/java/jenkins/security/s2m/JarURLValidatorImpl.java
@@ -0,0 +1,104 @@
+/*
+ * The MIT License
+ *
+ * Copyright (c) 2024 CloudBees, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package jenkins.security.s2m;
+
+import edu.umd.cs.findbugs.annotations.Nullable;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import hudson.Extension;
+import hudson.PluginManager;
+import hudson.remoting.Channel;
+import hudson.remoting.ChannelBuilder;
+import hudson.remoting.JarURLValidator;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import jenkins.model.Jenkins;
+import jenkins.security.ChannelConfigurator;
+import jenkins.util.SystemProperties;
+import org.kohsuke.accmod.Restricted;
+import org.kohsuke.accmod.restrictions.NoExternalUse;
+
+@Restricted(NoExternalUse.class)
+@Deprecated
+@Extension
+public class JarURLValidatorImpl extends ChannelConfigurator implements JarURLValidator {
+
+ public static final Logger LOGGER = Logger.getLogger(JarURLValidatorImpl.class.getName());
+
+ @Override
+ public void onChannelBuilding(ChannelBuilder builder, @Nullable Object context) {
+ LOGGER.log(Level.CONFIG, () -> "Setting up JarURLValidatorImpl for context: " + context);
+ builder.withProperty(JarURLValidator.class, this);
+ }
+
+ @Override
+ public void validate(URL url) throws IOException {
+ final String rejectAllProp = JarURLValidatorImpl.class.getName() + ".REJECT_ALL";
+ if (SystemProperties.getBoolean(rejectAllProp)) {
+ LOGGER.log(Level.FINE, () -> "Rejecting URL due to configuration: " + url);
+ throw new IOException("The system property '" + rejectAllProp + "' has been set, so all attempts by agents to load jars from the controller are rejected."
+ + " Update the agent.jar of the affected agent to a version released in August 2024 or later to prevent this error."); // TODO better version spec
+ }
+ final String allowAllProp = Channel.class.getName() + ".DISABLE_JAR_URL_VALIDATOR";
+ if (SystemProperties.getBoolean(allowAllProp)) {
+ LOGGER.log(Level.FINE, () -> "Allowing URL due to configuration: " + url);
+ return;
+ }
+ if (!isAllowedJar(url)) {
+ LOGGER.log(Level.FINE, () -> "Rejecting URL: " + url);
+ throw new IOException("This URL does not point to a jar file allowed to be requested by agents: " + url + "."
+ + " Update the agent.jar of the affected agent to a version released in August 2024 or later to prevent this error."
+ + " Alternatively, set the system property '" + allowAllProp + "' to 'true' if all the code built by Jenkins is as trusted as an administrator.");
+ } else {
+ LOGGER.log(Level.FINE, () -> "Allowing URL: " + url);
+ }
+ }
+ @SuppressFBWarnings(
+ value = "DMI_COLLECTION_OF_URLS",
+ justification = "All URLs point to local files, so no DNS lookup.")
+ private static boolean isAllowedJar(URL url) {
+ final ClassLoader classLoader = Jenkins.get().getPluginManager().uberClassLoader;
+ if (classLoader instanceof PluginManager.UberClassLoader uberClassLoader) {
+ if (uberClassLoader.isPluginJar(url)) {
+ LOGGER.log(Level.FINER, () -> "Determined to be plugin jar: " + url);
+ return true;
+ }
+ }
+
+ final ClassLoader coreClassLoader = Jenkins.class.getClassLoader();
+ if (coreClassLoader instanceof URLClassLoader urlClassLoader) {
+ if (Set.of(urlClassLoader.getURLs()).contains(url)) {
+ LOGGER.log(Level.FINER, () -> "Determined to be core jar: " + url);
+ return true;
+ }
+ }
+
+ LOGGER.log(Level.FINER, () -> "Neither core nor plugin jar: " + url);
+ return false;
+ }
+}
diff --git a/core/src/main/resources/hudson/Messages.properties b/core/src/main/resources/hudson/Messages.properties
index a0b5fd3edb0f..99c77ebbed46 100644
--- a/core/src/main/resources/hudson/Messages.properties
+++ b/core/src/main/resources/hudson/Messages.properties
@@ -67,7 +67,7 @@ PluginManager.UnexpectedException=Unexpected exception going through the retryin
PluginManager.compatWarning=\
- Warning: The new version of this plugin is marked as incompatible with the installed version. \
+ Warning: The new version of this plugin is marked as incompatible with the installed version of the plugin. \
This is usually the case because its behavior or APIs changed, or because it uses a different settings format than the installed version. \
Other plugins with a dependency on this plugin may be incompatible with this update and no longer work as expected, jobs using this plugin may need to be reconfigured, and/or you may not be able to cleanly revert to the prior version without manually restoring old settings. \
Consult the plugin release notes for details.
@@ -110,6 +110,7 @@ ProxyConfiguration.TestUrlRequired=Test URL is required.
ProxyConfiguration.MalformedTestUrl=Malformed Test URL {0}.
ProxyConfiguration.FailedToConnectViaProxy=Failed to connect to {0}.
ProxyConfiguration.FailedToConnect=Failed to connect to {0} (code {1}).
+ProxyConfiguration.NonTLSWarning=Jenkins only supports using an http connection to the proxy. The credentials may be exposed to someone on the same network.
ProxyConfiguration.Success=Success (code {0})
Functions.NoExceptionDetails=No Exception details
diff --git a/core/src/main/resources/hudson/Messages_es.properties b/core/src/main/resources/hudson/Messages_es.properties
index d1ae229de9bc..298ef23821d8 100644
--- a/core/src/main/resources/hudson/Messages_es.properties
+++ b/core/src/main/resources/hudson/Messages_es.properties
@@ -99,6 +99,7 @@ ProxyConfiguration.TestUrlRequired=Se requiere un URL de prueba.
ProxyConfiguration.MalformedTestUrl=La URL de prueba está mal formada.
ProxyConfiguration.FailedToConnectViaProxy=No se puede conectar a {0}.
ProxyConfiguration.FailedToConnect=No se puede conectar a {0} (código {1}).
+ProxyConfiguration.NonTLSWarning=Jenkins solo soporta conexiones http con el proxy. Las credenciales podrían quedar expuestas a cualquiera que se encuentre en la misma red.
ProxyConfiguration.Success=Configurado (código {0})
Functions.NoExceptionDetails=No hay detalles de la excepción
diff --git a/core/src/main/resources/hudson/Messages_fr.properties b/core/src/main/resources/hudson/Messages_fr.properties
index db45e76ab397..1e0286deddc5 100644
--- a/core/src/main/resources/hudson/Messages_fr.properties
+++ b/core/src/main/resources/hudson/Messages_fr.properties
@@ -109,6 +109,7 @@ ProxyConfiguration.TestUrlRequired=Une URL de test est requise.
ProxyConfiguration.MalformedTestUrl=L''URL de test {0} n''est pas correctement formée.
ProxyConfiguration.FailedToConnectViaProxy=Impossible de se connecter à {0}.
ProxyConfiguration.FailedToConnect=Impossible de se connecter à {0} (code {1}).
+ProxyConfiguration.NonTLSWarning=Jenkins ne prend en charge que l''utilisation d''une connexion http vers le proxy. Les informations d''identification peuvent être exposées à une personne qui se trouve sur le même réseau.
ProxyConfiguration.Success=Succès (code {0})
Functions.NoExceptionDetails=Aucun détail concernant l''exception
diff --git a/core/src/main/resources/hudson/Messages_it.properties b/core/src/main/resources/hudson/Messages_it.properties
index b929e6447cf8..2747e2d39366 100644
--- a/core/src/main/resources/hudson/Messages_it.properties
+++ b/core/src/main/resources/hudson/Messages_it.properties
@@ -106,6 +106,7 @@ PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Errore \
ProxyConfiguration.FailedToConnect=Impossibile connettersi a {0} (codice {1}).
ProxyConfiguration.FailedToConnectViaProxy=Impossibile connettersi a {0}.
ProxyConfiguration.MalformedTestUrl=URL di prova {0} malformato.
+ProxyConfiguration.NonTLSWarning=Jenkins supporta solo l''utilizzo di una connessione http al proxy. Le credenziali potrebbero essere esposte a qualcuno sulla stessa rete.
ProxyConfiguration.Success=Connessione riuscita (codice {0})
ProxyConfiguration.TestUrlRequired=È richiesto un URL di prova.
TcpSlaveAgentListener.PingAgentProtocol.displayName=Protocollo ping
diff --git a/core/src/main/resources/hudson/PluginManager/updates.properties b/core/src/main/resources/hudson/PluginManager/updates.properties
index 089679ac7573..521f5a01585f 100644
--- a/core/src/main/resources/hudson/PluginManager/updates.properties
+++ b/core/src/main/resources/hudson/PluginManager/updates.properties
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
compatWarning=\
- Warning: The new version of this plugin is marked as incompatible with the installed version. \
+ Warning: The new version of this plugin is marked as incompatible with the installed version of the plugin. \
This is usually the case because its behavior changed, or because it uses a different settings format than the installed version. \
Jobs using this plugin may need to be reconfigured, and/or you may not be able to cleanly revert to the prior version without manually restoring old settings. \
Consult the plugin release notes for details.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.jelly b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.jelly
index 04927bbc2757..c058c951439e 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.jelly
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.jelly
@@ -26,73 +26,67 @@ THE SOFTWARE.
Displays two projects side by side and show their relationship
-->
-
+
-
-
+
+
-
+
+
+ ${%body}
+
+
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.properties
new file mode 100644
index 000000000000..abe7b7fdcb00
--- /dev/null
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship.properties
@@ -0,0 +1,27 @@
+# The MIT License
+#
+# Copyright (c) 2024, Tim Jacomb
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+body=\
+ When you have projects that depend on each other, Jenkins can track which build of \
+ the upstream project is used by which build of the downstream project, by using \
+ the records created by \
+ the fingerprint support.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_bg.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_bg.properties
index 0f9e044632d5..b4ab15f888b8 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_bg.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_bg.properties
@@ -30,3 +30,9 @@ downstream\ project=\
Последващ проект
upstream\ project=\
Предхождащ проект
+body=\
+ Когато проекти зависят един от друг, Jenkins може да следи коя версия на\
+ предшестващ проект се ползва от следващ проект и обратно като създава база от\
+ данни от\
+ цифровите\
+ отпечатъци.
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_da.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_da.properties
index 11c9e45ff5e9..de46854c5245 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_da.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_da.properties
@@ -25,3 +25,6 @@ Project\ Relationship=Projektforhold
Compare=Sammenlign
downstream\ project=downstreamprojekt
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=Ingen opslag i filfingeraftryksdatabasen forbinder disse to projekter.
+body=\
+Har du projekter der er afhængige af hinanden kan Jenkins følge hvilket byg af upstreamprojektet \
+der bruges af hvilket byg af downstream projektet ved at bruge filfingeraftyrk
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_de.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_de.properties
index 06d92117599f..d23611e73c19 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_de.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_de.properties
@@ -26,3 +26,7 @@ downstream\ project=Nachgelagertes Projekt
Compare=Vergleichen
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=\
Es existieren keine Fingerabdrücke, welche diese beiden Projekte miteinander verbinden.
+body=\
+ Wenn Sie voneinander abhängige Projekte entwickeln, kann Jenkins für Sie herausfinden, welcher Build \
+ eines vorgelagerten Projektes für welchen Build eines nachgelagerten Projektes verwendet wurde. Dies geschieht über \
+ gespeicherte "Fingerabdrücke", die mit Hilfe der Fingerabdruck-Funktion erzeugt wurden.
\ No newline at end of file
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_es.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_es.properties
index e1d6bf40ea34..d9564f86be60 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_es.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_es.properties
@@ -25,4 +25,4 @@ upstream\ project=Proyecto padre
downstream\ project=Proyecto hijo
Compare=Comparar
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=No hay marcas que relacionen estos dos proyectos
-
+body=Cuando hay proyectos que dependen unos de otros, Jenkins puede hacer un seguimiento de qué proyectos padres están siendo utilizado por otros proyectos hijos usando un registro de firmas de los ficheros generados. Echa un vistazo a esta pagina: the fingerprint support.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_et.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_et.properties
index b6e5aab3f6c0..0f97ea179c01 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_et.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_et.properties
@@ -4,3 +4,4 @@ Compare=Võrdle
Project\ Relationship=Projektide seosed
downstream\ project=allavoolu projekt
upstream\ project=ülesvoolu projekt
+body=Kui teil on kaks projekti mis sõltuvad üksteisest, siis suudab Jenkins jälgida seda millist ülesvoolu projekti järku kasutatakse mingi allavoolu projekti järgu jaoks, kasutades sõrmejälje toe poolt loodud kirjeid.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_fr.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_fr.properties
index 4bf5265db82e..0e59b408f513 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_fr.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_fr.properties
@@ -24,3 +24,8 @@ Project\ Relationship=Relations entre les projets
upstream\ project=Projet en amont
downstream\ project=Projet en aval
Compare=Comparer
+body=\
+ Lorsque vous avez des projets qui dépendent les uns des autres, Jenkins peut tracer quel build \
+ de projet en amont est utilisé par quel build de projet en aval, en utilisant \
+ les enregistrements créés par \
+ le support de l''empreinte numérique.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_it.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_it.properties
index 72817d4b846f..059bb1d81d8a 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_it.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_it.properties
@@ -27,3 +27,8 @@ Project\ Relationship=Relazioni progetto
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=\
Non ci sono record di impronte digitali che colleghino questi due progetti.
upstream\ project=progetto upstream
+body=Quando si hanno dei progetti dipendenti fra loro, Jenkins può tener \
+ traccia della compilazione del progetto upstream che è stata utilizzata in \
+ una precisa compilazione del progetto downstream utilizzando i record \
+ creati dal supporto per \
+ le impronte digitali.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ja.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ja.properties
index ff5a73f5237b..ad6ec2424289 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ja.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ja.properties
@@ -26,3 +26,7 @@ downstream\ project=下流プロジェクト
Compare=比較
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=\
2つのプロジェクトを関連付けるファイル指紋の記録がありません。
+body=\
+ 互いに依存するプロジェクトがある場合、 Jenkinsはどの上流プロジェクトがどの下流プロジェクトに使用されているかを、\
+ 指紋サポート\
+ によって作成された記録を使用することで追跡することができます。
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_lt.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_lt.properties
index 7343b885121f..9393134612fa 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_lt.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_lt.properties
@@ -3,3 +3,7 @@ Project\ Relationship=Projektų ryšys
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=Nėra antspaudų, siejančių šiuos du projektus.
downstream\ project=vėlesnis projektas
upstream\ project=ankstesnis projektas
+body=\
+ Kai turite projektus, kurie priklauso vienas nuo kito, naudodamas įrašus, sukurtus \
+ pirštų antspaudų palaikymo Jenkinsas gali sekti, kuris ankstesnio projekto vykdymas \
+ naudojamas kuriame žemesnio projekto vykdyme.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_nl.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_nl.properties
index 5c6ed916c025..a3c300d6466b 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_nl.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_nl.properties
@@ -24,3 +24,8 @@ Project\ Relationship=Relaties tussen projecten
upstream\ project=bovenliggende projecten
downstream\ project=onderliggende projecten
Compare=Vergelijk
+body=\
+ Wanneer je projecten ontwikkelt die van elkaar afhankelijk zijn, kan Jenkins voor jou uitzoeken welke \
+ bouwpoging van een bovenliggend project gebruikt wordt door een onderliggend project. Dit gebeurt aan \
+ de hand van de geregistreerd elektronische vingerafdrukken van \
+ de door een bouwpoging opgeleverde artefacten.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_pt_BR.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_pt_BR.properties
index e00d7e50887b..c8c0fef9ad00 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_pt_BR.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_pt_BR.properties
@@ -25,3 +25,6 @@ upstream\ project=Projeto pai
downstream\ project=Projeto filho
Compare=Comparar
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=Não existe identificador conectando esses dois projetos.
+body=Quando você tem projetos que dependem um do outro, o Jenkins pode rastrear qual construção é hierarquicamente \
+ superior, usando os registros criados pelo suporte de \
+ impressão digital.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ru.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ru.properties
index 0f8b1dc929db..b963beddd762 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ru.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_ru.properties
@@ -25,3 +25,7 @@ upstream\ project=восходящий проект
downstream\ project=нисходящий проект
Compare=Сравнить
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=Нет отпечатков (fingerprints), соединяющих эти два проекта.
+body=\
+ Когда у вас есть проекты, один из которых зависит от другого, Jenkins может отслеживать, \
+ какая сборка восходящего проекта использована в какой сборке нисходящего, используя \
+ сохраненные записи отпечатков.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sr.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sr.properties
index 1c8482f8276e..2a1cb820955b 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sr.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sr.properties
@@ -5,3 +5,4 @@ downstream\ project=downstream пројекат
Compare=Упореди
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=
Project\ Relationship=Однос пројеката
+body=Jenkins може пратити изградњу пројекте који зависе један од другог користећи дигитални отисак.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sv_SE.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sv_SE.properties
index f04eb1c1b402..72ff5db1ce2b 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sv_SE.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_sv_SE.properties
@@ -5,3 +5,7 @@ Project\ Relationship=Projektrelation
downstream\ project=nedströmsprojekt
upstream\ project=uppströmsprojekt
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=Det finns inga registrerade fingeravtryck som kopplar samman dessa två projekt.
+body=\
+ När du har projekt som är beroende av varandra kan Jenkins spåra vilket bygge \
+ av uppströmsprojektet som används av vilket bygge av nedströmsprojektet \
+ genom att använda de poster som skapats av fingeravtrycksstödet.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_tr.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_tr.properties
index a16167154de0..8aaa86a2532d 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_tr.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_tr.properties
@@ -24,3 +24,7 @@ Project\ Relationship=Projelerin İlişkisi
upstream\ project=upstream proje
downstream\ project=downstream proje
Compare=Karşılaştır
+body=\
+ Eğer birbirine bağlı projeleriniz varsa, Jenkins parmakizi desteği\
+ ile oluşturulan kayıtları kullanarak hangi upstream projenin hangi downstream proje tarafından\
+ kullanıldığını takip edebilir.
diff --git a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_zh_TW.properties b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_zh_TW.properties
index f91119563d0d..889793249988 100644
--- a/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_zh_TW.properties
+++ b/core/src/main/resources/jenkins/model/Jenkins/projectRelationship_zh_TW.properties
@@ -27,3 +27,4 @@ downstream\ project=下游專案
Compare=比較
There\ are\ no\ fingerprint\ records\ that\ connect\ these\ two\ projects.=\
沒有連結這兩個專案的指紋記錄。
+body=如果您的專案間彼此有關聯,Jenkins 就能追蹤下游專案到底是使用到上游專案的哪一版進行建置。這個功能是利用檔案指紋功能所產生的記錄來達成。
diff --git a/core/src/main/resources/lib/hudson/executors.jelly b/core/src/main/resources/lib/hudson/executors.jelly
index f7ed2b22b70a..6b1775ce34a3 100644
--- a/core/src/main/resources/lib/hudson/executors.jelly
+++ b/core/src/main/resources/lib/hudson/executors.jelly
@@ -143,7 +143,7 @@ THE SOFTWARE.
and !builtInHasExecutors)}"/>
-
+
diff --git a/core/src/site/site.xml b/core/src/site/site.xml
index 9f5eb7448565..56902cecd2a1 100644
--- a/core/src/site/site.xml
+++ b/core/src/site/site.xml
@@ -6,7 +6,7 @@
+
diff --git a/pom.xml b/pom.xml
index c44d78efd74e..15bf19ad373f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,6 +53,7 @@ THE SOFTWARE.
bomwebsocket/spiwebsocket/jetty10
+ websocket/jetty12-ee8corewartest
@@ -73,9 +74,9 @@ THE SOFTWARE.
- 2.471
+ 2.473-SNAPSHOT
- 2024-07-30T17:24:44Z
+ 2024-08-13T13:50:51Zgithub
@@ -87,19 +88,17 @@ THE SOFTWARE.
https://www.jenkins.io/changelog
- 3256.v88a_f6e922152
-
- 3107.v665000b_51092
+ 3261.v9c670a_4748a_9MaxMedium1.33
- 4.13.1
+ 4.13.21.29false
- 6.21
+ 7.0
+ 3107.v665000b_51092io.jenkins.pluginscommons-text-api
- 1.12.0-119.v73ef73f2345d
+ 1.12.0-129.v99a_50df237f7
@@ -93,7 +95,7 @@ THE SOFTWARE.
org.jenkins-ci.pluginsant
- 497.v94e7d9fffa_b_9
+ 511.v0a_a_1a_334f41b_org.jenkins-ci.plugins
@@ -121,7 +123,7 @@ THE SOFTWARE.
org.jenkins-ci.plugins.workflowworkflow-api
- 1322.v857eeeea_9902
+ 1332.vc21122317a_8eorg.jenkins-ci.plugins.workflow
@@ -147,7 +149,7 @@ THE SOFTWARE.
${project.groupId}jenkins-test-harness
- 2225.2230.v6210cb_b_827f9
+ 2250.v03a_1295b_0a_30test
@@ -188,7 +190,7 @@ THE SOFTWARE.
org.awaitilityawaitility
- 4.2.1
+ 4.2.2test
@@ -228,7 +230,7 @@ THE SOFTWARE.
org.jenkins-ci.pluginsjunit
- 1280.v310a_78b_9a_1e0
+ 1284.vf75d778f98c5test
@@ -323,6 +325,14 @@ THE SOFTWARE.
${project.build.outputDirectory}/old-remotingremoting-minimum-supported.jar
+
+ org.jenkins-ci.main
+ remoting
+ 3256.v88a_f6e922152
+ jar
+ ${project.build.outputDirectory}/old-remoting
+ remoting-before-SECURITY-3430-fix.jar
+ org.jenkins-ci.mainremoting
@@ -475,5 +485,11 @@ THE SOFTWARE.
+
+ release
+
+ 1
+
+
diff --git a/test/src/test/java/hudson/PluginTest.java b/test/src/test/java/hudson/PluginTest.java
index 0e4192ae4bf8..79cf3703a39a 100644
--- a/test/src/test/java/hudson/PluginTest.java
+++ b/test/src/test/java/hudson/PluginTest.java
@@ -54,7 +54,7 @@ public class PluginTest {
r.createWebClient().assertFails("plugin/matrix-auth/images/%2e%2e%2fWEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/matrix-auth/images/%2e.%2fWEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/matrix-auth/images/..%2f..%2f..%2f" + r.jenkins.getRootDir().getName() + "%2fsecrets%2fmaster.key", HttpServletResponse.SC_BAD_REQUEST);
- r.createWebClient().assertFails("plugin/matrix-auth/" + r.jenkins.getRootDir() + "/secrets/master.key", /* ./ prepended anyway */ HttpServletResponse.SC_NOT_FOUND);
+ r.createWebClient().assertFails("plugin/matrix-auth/" + r.jenkins.getRootDir() + "/secrets/master.key", /* ./ prepended anyway */ Functions.isWindows() ? HttpServletResponse.SC_BAD_REQUEST : HttpServletResponse.SC_NOT_FOUND);
// SECURITY-155:
r.createWebClient().assertFails("plugin/matrix-auth/WEB-INF/licenses.xml", HttpServletResponse.SC_BAD_REQUEST);
r.createWebClient().assertFails("plugin/matrix-auth/META-INF/MANIFEST.MF", HttpServletResponse.SC_BAD_REQUEST);
diff --git a/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java b/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java
index e73cbec70359..24c978e78f8d 100644
--- a/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java
+++ b/test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java
@@ -149,8 +149,16 @@ public void doubleDots2() throws Exception {
p.getBuildersList().add(new Shell("mkdir abc; touch abc/def.bin"));
j.buildAndAssertSuccess(p);
- // can we see it?
- j.createWebClient().goTo("job/" + p.getName() + "/ws/abc%5Cdef.bin", "application/octet-stream");
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ // normal path provided by the UI succeeds
+ wc.goTo("job/" + p.getName() + "/ws/abc/def.bin", "application/octet-stream");
+
+ // suspicious path is rejected with 400
+ wc.setThrowExceptionOnFailingStatusCode(false);
+ HtmlPage page = wc.goTo("job/" + p.getName() + "/ws/abc%5Cdef.bin");
+ assertEquals(400, page.getWebResponse().getStatusCode());
+ assertEquals("Error 400 Suspicious Path Character", page.getTitleText());
+ }
}
@Test
@@ -1108,10 +1116,13 @@ public void windows_cannotViewAbsolutePath() throws Exception {
String content = "random data provided as fixed value";
Files.writeString(targetTmpPath, content, StandardCharsets.UTF_8);
- JenkinsRule.WebClient wc = j.createWebClient().withThrowExceptionOnFailingStatusCode(false);
- Page page = wc.goTo("userContent/" + targetTmpPath.toAbsolutePath() + "/*view*", null);
-
- MatcherAssert.assertThat(page.getWebResponse().getStatusCode(), equalTo(404));
+ try (JenkinsRule.WebClient wc = j.createWebClient()) {
+ // suspicious path is rejected with 400
+ wc.setThrowExceptionOnFailingStatusCode(false);
+ HtmlPage page = wc.goTo("userContent/" + targetTmpPath.toAbsolutePath() + "/*view*");
+ assertEquals(400, page.getWebResponse().getStatusCode());
+ assertEquals("Error 400 Suspicious Path Character", page.getTitleText());
+ }
}
@Test
diff --git a/test/src/test/java/hudson/model/Security3349Test.java b/test/src/test/java/hudson/model/Security3349Test.java
new file mode 100644
index 000000000000..6de55eb653dd
--- /dev/null
+++ b/test/src/test/java/hudson/model/Security3349Test.java
@@ -0,0 +1,80 @@
+package hudson.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import jenkins.model.Jenkins;
+import org.htmlunit.html.HtmlPage;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.FlagRule;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.MockAuthorizationStrategy;
+
+public class Security3349Test {
+
+ @Rule
+ public JenkinsRule rule = new JenkinsRule();
+
+ @Rule public FlagRule skipPermissionCheck = new FlagRule<>(() -> MyViewsProperty.SKIP_PERMISSION_CHECK, x -> MyViewsProperty.SKIP_PERMISSION_CHECK = x);
+
+ @Test
+ @Issue("SECURITY-3349")
+ public void usersCannotAccessOtherUsersViews() throws Exception {
+ User user = User.getOrCreateByIdOrFullName("user");
+ User admin = User.getOrCreateByIdOrFullName("admin");
+
+ rule.jenkins.setSecurityRealm(rule.createDummySecurityRealm());
+ MockAuthorizationStrategy mockAuthorizationStrategy = new MockAuthorizationStrategy();
+ mockAuthorizationStrategy.grant(Jenkins.READ, View.READ).everywhere().to("user");
+ mockAuthorizationStrategy.grant(Jenkins.ADMINISTER).everywhere().to("admin");
+ rule.jenkins.setAuthorizationStrategy(mockAuthorizationStrategy);
+
+ MyViewsProperty prop1 = new MyViewsProperty(null);
+ MyView usersView = new MyView("User's view", prop1);
+ user.addProperty(prop1);
+ prop1.setUser(user);
+ prop1.addView(usersView);
+
+ MyViewsProperty prop2 = new MyViewsProperty(null);
+ MyView adminsView = new MyView("Admin's view", prop2);
+ admin.addProperty(prop2);
+ prop2.setUser(admin);
+ prop2.addView(adminsView);
+
+ try (JenkinsRule.WebClient wc = rule.createWebClient()) {
+ wc.setThrowExceptionOnFailingStatusCode(false);
+ wc.login("user");
+
+ HtmlPage adminViews = wc.goTo("user/admin/my-views/view/all/");
+ assertEquals(403, adminViews.getWebResponse().getStatusCode());
+
+ HtmlPage adminUserPage = wc.goTo("user/admin/");
+ assertFalse(adminUserPage.getWebResponse().getContentAsString().contains("My Views"));
+
+ HtmlPage userViews = wc.goTo("user/user/my-views/view/all/");
+ assertEquals(200, userViews.getWebResponse().getStatusCode());
+
+ HtmlPage userUserPage = wc.goTo("user/user/");
+ assertTrue(userUserPage.getWebResponse().getContentAsString().contains("My Views"));
+
+ wc.login("admin");
+
+ adminViews = wc.goTo("user/admin/my-views/view/all/");
+ assertEquals(200, adminViews.getWebResponse().getStatusCode());
+ userViews = wc.goTo("user/user/my-views/view/all/");
+ assertEquals(200, userViews.getWebResponse().getStatusCode());
+
+ MyViewsProperty.SKIP_PERMISSION_CHECK = true;
+
+ wc.login("user");
+ adminViews = wc.goTo("user/admin/my-views/view/all/");
+ assertEquals(200, adminViews.getWebResponse().getStatusCode());
+ adminUserPage = wc.goTo("user/admin/");
+ assertTrue(adminUserPage.getWebResponse().getContentAsString().contains("My Views"));
+
+ }
+ }
+}
diff --git a/test/src/test/java/hudson/model/UpdateSiteTest.java b/test/src/test/java/hudson/model/UpdateSiteTest.java
index 5902387c2e83..629c7c7f46e8 100644
--- a/test/src/test/java/hudson/model/UpdateSiteTest.java
+++ b/test/src/test/java/hudson/model/UpdateSiteTest.java
@@ -55,16 +55,19 @@
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import jenkins.model.Jenkins;
import jenkins.security.UpdateSiteWarningsConfiguration;
import jenkins.security.UpdateSiteWarningsMonitor;
import org.apache.commons.io.FilenameUtils;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -115,19 +118,21 @@ public void setUpWebServer() throws Exception {
server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);
- server.setHandler(new AbstractHandler() {
+ server.setHandler(new Handler.Abstract() {
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
+ public boolean handle(Request request, Response response, Callback callback) throws IOException {
+ String target = request.getHttpURI().getPath();
if (target.startsWith(RELATIVE_BASE)) {
target = target.substring(RELATIVE_BASE.length());
}
String responseBody = getResource(target);
if (responseBody != null) {
- baseRequest.setHandled(true);
- response.setContentType("text/plain; charset=utf-8");
- response.setStatus(HttpServletResponse.SC_OK);
- response.getOutputStream().write(responseBody.getBytes(StandardCharsets.UTF_8));
+ response.getHeaders().add(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
+ response.setStatus(HttpStatus.OK_200);
+ Content.Sink.write(response, true, responseBody, callback);
+ return true;
}
+ return false;
}
});
server.start();
diff --git a/test/src/test/java/jenkins/install/SetupWizardTest.java b/test/src/test/java/jenkins/install/SetupWizardTest.java
index 7168f93d93af..299937df462e 100644
--- a/test/src/test/java/jenkins/install/SetupWizardTest.java
+++ b/test/src/test/java/jenkins/install/SetupWizardTest.java
@@ -50,15 +50,17 @@
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import jenkins.model.Jenkins;
import jenkins.util.JSONSignatureValidator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.Content;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.Callback;
import org.htmlunit.Page;
import org.junit.Before;
import org.junit.Rule;
@@ -336,7 +338,7 @@ protected JSONSignatureValidator getJsonSignatureValidator(String name) {
}
}
- private static class RemoteUpdateSiteHandler extends AbstractHandler {
+ private static class RemoteUpdateSiteHandler extends Handler.Abstract {
private String serverContext;
private boolean includeSignature;
@@ -347,15 +349,18 @@ private static class RemoteUpdateSiteHandler extends AbstractHandler {
}
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
- String responseBody = getWebServerResource(target, request.getParameter("version"));
+ public boolean handle(Request request, Response response, Callback callback) throws IOException {
+ String target = request.getHttpURI().getPath();
+ String version = Request.extractQueryParameters(request).get("version").getValue();
+ String responseBody = getWebServerResource(target, version);
if (responseBody != null) {
- baseRequest.setHandled(true);
- response.setContentType("text/plain; charset=utf-8");
- response.setStatus(HttpServletResponse.SC_OK);
- response.getOutputStream().write(responseBody.getBytes(StandardCharsets.UTF_8));
+ response.getHeaders().add(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
+ response.setStatus(HttpStatus.OK_200);
+ Content.Sink.write(response, true, responseBody, callback);
+ return true;
} else {
- response.sendError(404);
+ Response.writeError(request, response, callback, HttpStatus.NOT_FOUND_404);
+ return true;
}
}
diff --git a/test/src/test/java/jenkins/security/Security3030Test.java b/test/src/test/java/jenkins/security/Security3030Test.java
index 919faa5d7da2..5e712bdc4e31 100644
--- a/test/src/test/java/jenkins/security/Security3030Test.java
+++ b/test/src/test/java/jenkins/security/Security3030Test.java
@@ -272,6 +272,14 @@ public HttpResponse doSubmitMultipart(StaplerRequest req) throws FileUploadExcep
return processMultipartAndUnwrap(req);
} else {
actualWrapped = Assert.assertThrows(expectedWrapped, () -> processMultipartAndUnwrap(req));
+
+ // The client might still be sending us more of the request, but we have had enough of it already and
+ // have decided to stop processing it. Drain the read end of the socket so that the client can finish
+ // sending its request in order to read the response we are about to provide.
+ try (OutputStream os = OutputStream.nullOutputStream()) {
+ req.getInputStream().transferTo(os);
+ }
+
return HttpResponses.ok();
}
}
diff --git a/test/src/test/java/jenkins/security/Security3430Test.java b/test/src/test/java/jenkins/security/Security3430Test.java
new file mode 100644
index 000000000000..f39702fd4204
--- /dev/null
+++ b/test/src/test/java/jenkins/security/Security3430Test.java
@@ -0,0 +1,295 @@
+package jenkins.security;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import hudson.ExtensionList;
+import hudson.model.Computer;
+import hudson.remoting.Channel;
+import hudson.remoting.Launcher;
+import hudson.slaves.SlaveComputer;
+import hudson.util.RingBufferLogHandler;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.Security;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import jenkins.bouncycastle.api.InstallBouncyCastleJCAProvider;
+import jenkins.security.s2m.JarURLValidatorImpl;
+import jenkins.slaves.RemotingVersionInfo;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.InboundAgentRule;
+import org.jvnet.hudson.test.JenkinsRule;
+import org.jvnet.hudson.test.RealJenkinsRule;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.stapler.Stapler;
+
+public class Security3430Test {
+ @Rule
+ public RealJenkinsRule jj = new RealJenkinsRule().withLogger(JarURLValidatorImpl.class, Level.FINEST);
+
+ @Rule
+ public InboundAgentRule agents = new InboundAgentRule();
+
+ @Test
+ public void runWithOldestSupportedAgentJar() throws Throwable {
+ runWithRemoting(RemotingVersionInfo.getMinimumSupportedVersion().toString(), "/old-remoting/remoting-minimum-supported.jar", true);
+ }
+
+ @Test
+ public void runWithPreviousAgentJar() throws Throwable {
+ runWithRemoting("3256.v88a_f6e922152", "/old-remoting/remoting-before-SECURITY-3430-fix.jar", true);
+ }
+
+ @Test
+ public void runWithCurrentAgentJar() throws Throwable {
+ runWithRemoting(null, null, false);
+ }
+
+ private void runWithRemoting(String expectedRemotingVersion, String remotingResourcePath, boolean requestingJarFromAgent) throws Throwable {
+ if (expectedRemotingVersion != null) {
+ FileUtils.copyURLToFile(Security3430Test.class.getResource(remotingResourcePath), new File(jj.getHome(), "agent.jar"));
+ }
+
+ jj.startJenkins();
+ final String agentName = "agent1";
+ try {
+ agents.createAgent(jj, InboundAgentRule.Options.newBuilder().name(agentName).build());
+ jj.runRemotely(Security3430Test::_run, agentName, expectedRemotingVersion, requestingJarFromAgent, true);
+ } finally {
+ agents.stop(jj, agentName);
+ }
+ jj.runRemotely(Security3430Test::disableJarURLValidatorImpl);
+ final String agentName2 = "agent2";
+ try {
+ agents.createAgent(jj, InboundAgentRule.Options.newBuilder().name(agentName2).build());
+ jj.runRemotely(Security3430Test::_run, agentName2, expectedRemotingVersion, requestingJarFromAgent, false);
+ } finally {
+ agents.stop(jj, agentName2);
+ }
+ }
+
+ // This is quite artificial, but demonstrating that without JarURLValidatorImpl we do not allow any calls from the agent:
+ private static void disableJarURLValidatorImpl(JenkinsRule j) {
+ assertTrue(ExtensionList.lookup(ChannelConfigurator.class).remove(ExtensionList.lookupSingleton(JarURLValidatorImpl.class)));
+ }
+
+ /**
+ *
+ * @param agentName the name of the agent we're working with
+ * @param expectedRemotingVersion The version expected for remoting, or {@code null} if we're using whatever is bundled with this Jenkins.
+ * @param requestingJarFromAgent {@code true} if and only if we expect to go through {@code ClassLoaderProxy#fetchJar}
+ * @param hasJenkinsJarURLValidator {@code true} if and only we do not expect {@link jenkins.security.s2m.JarURLValidatorImpl} to be present. Only relevant when {@code requestingJarFromAgent} is {@code true}.
+ */
+ private static void _run(JenkinsRule j, String agentName, String expectedRemotingVersion, Boolean requestingJarFromAgent, Boolean hasJenkinsJarURLValidator) throws Throwable {
+ final RingBufferLogHandler logHandler = new RingBufferLogHandler(50);
+ Logger.getLogger(JarURLValidatorImpl.class.getName()).addHandler(logHandler);
+ final List logRecords = logHandler.getView();
+
+ final Computer computer = j.jenkins.getComputer(agentName);
+ assertThat(computer, instanceOf(SlaveComputer.class));
+ SlaveComputer agent = (SlaveComputer) computer;
+ final Channel channel = agent.getChannel();
+ if (expectedRemotingVersion != null) {
+ final String result = channel.call(new AgentVersionCallable());
+ assertThat(result, is(expectedRemotingVersion));
+ }
+
+ logHandler.clear();
+
+ { // regular behavior
+ if (hasJenkinsJarURLValidator) {
+ // it works
+ assertTrue(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, Stapler.class));
+ if (requestingJarFromAgent) {
+ assertThat(logRecords, hasItem(logMessageContainsString("Allowing URL: file:/")));
+ } else {
+ assertThat(logRecords, is(empty()));
+ }
+
+ logHandler.clear();
+ assertFalse(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, Stapler.class));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Allowing URL"))));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Rejecting URL"))));
+ } else {
+ // outdated remoting.jar will fail, but up to date one passes
+ if (requestingJarFromAgent) {
+ final IOException ex = assertThrows(IOException.class, () -> channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, Stapler.class));
+ assertThat(ex.getMessage(), containsString("No hudson.remoting.JarURLValidator has been set for this channel, so all #fetchJar calls are rejected. This is likely a bug in Jenkins. As a workaround, try updating the agent.jar file."));
+ } else {
+ assertTrue(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, Stapler.class));
+ assertThat(logRecords, is(empty()));
+ }
+ }
+ }
+
+ logHandler.clear();
+
+ if (hasJenkinsJarURLValidator) { // Start rejecting everything; only applies to JarURLValidatorImpl
+ System.setProperty(JarURLValidatorImpl.class.getName() + ".REJECT_ALL", "true");
+
+ // Identify that a jar was already loaded:
+ assertFalse(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, Stapler.class));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Allowing URL"))));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Rejecting URL"))));
+
+ logHandler.clear();
+
+ // different jar file than before, old remoting will fail due to call through ClassLoaderProxy#fetchJar, new remoting passes
+ if (requestingJarFromAgent) {
+ final IOException ioException = assertThrows(IOException.class, () -> channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, Argument.class));
+ assertThat(ioException.getMessage(), containsString("all attempts by agents to load jars from the controller are rejected"));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Allowing URL"))));
+ assertThat(logRecords, hasItem(logMessageContainsString("Rejecting URL due to configuration: ")));
+ } else {
+ assertTrue(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, org.kohsuke.args4j.Argument.class));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Allowing URL"))));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Rejecting URL"))));
+ }
+ }
+
+ logHandler.clear();
+
+ if (hasJenkinsJarURLValidator) { // Disable block, only applies to JarURLValidatorImpl
+ System.clearProperty(JarURLValidatorImpl.class.getName() + ".REJECT_ALL");
+ if (requestingJarFromAgent) {
+ // now it works again for old remoting:
+ assertTrue(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, org.kohsuke.args4j.Argument.class));
+ assertThat(logRecords, hasItem(logMessageContainsString("Allowing URL: file:/")));
+ } else {
+ // new remoting already has it.
+ assertFalse(channel.preloadJar(j.jenkins.getPluginManager().uberClassLoader, org.kohsuke.args4j.Argument.class));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Allowing URL"))));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Rejecting URL"))));
+ }
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Rejecting URL due to configuration: "))));
+ }
+
+ logHandler.clear();
+
+ if (hasJenkinsJarURLValidator || !requestingJarFromAgent) { // prepare bouncycastle-api
+ assertTrue(j.jenkins.getPluginManager().getPlugin("bouncycastle-api").isActive());
+ InstallBouncyCastleJCAProvider.on(channel);
+ channel.call(new ConfirmBouncyCastleLibrary());
+ }
+
+ logHandler.clear();
+
+ { // Exploitation tests
+ final URL secretKeyFile = new File(j.jenkins.getRootDir(), "secret.key").toURI().toURL();
+ final String expectedContent = IOUtils.toString(secretKeyFile, StandardCharsets.UTF_8);
+ { // Protection is effective when agents request non-jar files:
+ final InvocationTargetException itex = assertThrows(InvocationTargetException.class, () -> channel.call(new Exploit(secretKeyFile, expectedContent)));
+ assertThat(itex.getCause(), instanceOf(IOException.class));
+ if (hasJenkinsJarURLValidator) {
+ assertThat(itex.getCause().getMessage(), containsString("This URL does not point to a jar file allowed to be requested by agents"));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Allowing URL"))));
+ assertThat(logRecords, hasItem(logMessageContainsString("Rejecting URL: ")));
+ } else {
+ assertThat(itex.getCause().getMessage(), containsString("No hudson.remoting.JarURLValidator has been set for this channel, so all #fetchJar calls are rejected. This is likely a bug in Jenkins. As a workaround, try updating the agent.jar file."));
+ }
+ }
+
+ logHandler.clear();
+
+ { // Disable protection and non-jar files can be accessed:
+ System.setProperty(Channel.class.getName() + ".DISABLE_JAR_URL_VALIDATOR", "true");
+ channel.call(new Exploit(secretKeyFile, expectedContent));
+ if (hasJenkinsJarURLValidator) {
+ assertThat(logRecords, hasItem(logMessageContainsString("Allowing URL due to configuration")));
+ assertThat(logRecords, not(hasItem(logMessageContainsString("Rejecting URL"))));
+ }
+ System.clearProperty(Channel.class.getName() + ".DISABLE_JAR_URL_VALIDATOR");
+ }
+ }
+ }
+
+ private static class AgentVersionCallable extends MasterToSlaveCallable {
+ @Override
+ public String call() throws Exception {
+ return Launcher.VERSION;
+ }
+ }
+
+ private static class ConfirmBouncyCastleLibrary extends MasterToSlaveCallable {
+ @Override
+ public Void call() throws Exception {
+ assertNotNull(Security.getProvider("BC"));
+ return null;
+ }
+ }
+
+ private static class Exploit extends MasterToSlaveCallable {
+ private final URL controllerFilePath;
+ private final String expectedContent;
+
+ public Exploit(URL controllerFilePath, String expectedContent) {
+ this.controllerFilePath = controllerFilePath;
+ this.expectedContent = expectedContent;
+ }
+ @Override
+ public Void call() throws Exception {
+ final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
+ final Field classLoaderProxyField = ccl.getClass().getDeclaredField("proxy");
+ classLoaderProxyField.setAccessible(true);
+ final Object theProxy = classLoaderProxyField.get(ccl);
+ final Method fetchJarMethod = theProxy.getClass().getDeclaredMethod("fetchJar", URL.class);
+ fetchJarMethod.setAccessible(true);
+ final byte[] fetchJarResponse = (byte[]) fetchJarMethod.invoke(theProxy, controllerFilePath);
+ assertThat(new String(fetchJarResponse, StandardCharsets.UTF_8), is(expectedContent));
+ return null;
+ }
+ }
+
+ // Would be nice if LoggerRule#recorded equivalents existed for use without LoggerRule, meanwhile:
+ private static Matcher logMessageContainsString(String needle) {
+ return new LogMessageContainsString(containsString(needle));
+ }
+
+ private static final class LogMessageContainsString extends TypeSafeMatcher {
+ private final Matcher stringMatcher;
+
+ public LogMessageContainsString(Matcher stringMatcher) {
+ this.stringMatcher = stringMatcher;
+ }
+
+ @Override
+ protected boolean matchesSafely(LogRecord item) {
+ return stringMatcher.matches(item.getMessage());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("a LogRecord with a message matching ");
+ stringMatcher.describeTo(description);
+ }
+
+ @Override
+ protected void describeMismatchSafely(LogRecord item, Description mismatchDescription) {
+ mismatchDescription.appendText("a LogRecord with the message: ");
+ mismatchDescription.appendText(item.getMessage());
+ }
+ }
+}
diff --git a/test/src/test/java/jenkins/util/SystemPropertiesTest.java b/test/src/test/java/jenkins/util/SystemPropertiesTest.java
index 4a0a2a78e981..d0b498f88e57 100644
--- a/test/src/test/java/jenkins/util/SystemPropertiesTest.java
+++ b/test/src/test/java/jenkins/util/SystemPropertiesTest.java
@@ -29,7 +29,7 @@
import static org.hamcrest.Matchers.nullValue;
import javax.servlet.ServletContextEvent;
-import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.ee8.webapp.WebAppContext;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assume;
@@ -103,7 +103,7 @@ public void shouldReturnWebAppPropertyIfSystemPropertyNotSetAndDefaultIsSet() th
* @param value value of the property
*/
protected void setWebAppInitParameter(String property, String value) {
- Assume.assumeThat(j.jenkins.servletContext, Matchers.instanceOf(ContextHandler.Context.class));
- ((ContextHandler.Context) j.jenkins.servletContext).getContextHandler().getInitParams().put(property, value);
+ Assume.assumeThat(j.jenkins.servletContext, Matchers.instanceOf(WebAppContext.Context.class));
+ ((WebAppContext.Context) j.jenkins.servletContext).getContextHandler().getInitParams().put(property, value);
}
}
diff --git a/war/package.json b/war/package.json
index c1dba52a2464..94aed9e08088 100644
--- a/war/package.json
+++ b/war/package.json
@@ -24,28 +24,28 @@
},
"devDependencies": {
"@babel/cli": "7.24.8",
- "@babel/core": "7.24.9",
- "@babel/preset-env": "7.25.0",
- "@eslint/js": "9.8.0",
+ "@babel/core": "7.25.2",
+ "@babel/preset-env": "7.25.3",
+ "@eslint/js": "9.9.0",
"babel-loader": "9.1.3",
"clean-webpack-plugin": "4.0.0",
"css-loader": "7.1.2",
"css-minimizer-webpack-plugin": "7.0.0",
- "eslint": "9.8.0",
+ "eslint": "9.9.0",
"eslint-config-prettier": "9.1.0",
"eslint-formatter-checkstyle": "8.40.0",
- "globals": "15.8.0",
+ "globals": "15.9.0",
"handlebars-loader": "1.7.3",
"mini-css-extract-plugin": "2.9.0",
- "postcss": "8.4.40",
+ "postcss": "8.4.41",
"postcss-loader": "8.1.1",
- "postcss-preset-env": "9.6.0",
+ "postcss-preset-env": "10.0.0",
"postcss-scss": "4.0.9",
"prettier": "3.3.3",
"sass": "1.77.8",
"sass-loader": "16.0.0",
"style-loader": "4.0.0",
- "stylelint": "16.7.0",
+ "stylelint": "16.8.1",
"stylelint-checkstyle-reporter": "1.0.0",
"stylelint-config-standard": "36.0.1",
"webpack": "5.93.0",
@@ -65,5 +65,5 @@
"defaults",
"not IE 11"
],
- "packageManager": "yarn@4.3.1"
+ "packageManager": "yarn@4.4.0"
}
diff --git a/war/pom.xml b/war/pom.xml
index 08c1aa61a3b9..41b877f41a25 100644
--- a/war/pom.xml
+++ b/war/pom.xml
@@ -47,12 +47,14 @@ THE SOFTWARE.
80802.13.1-117.v2f1a_b_66ff91d
+
+ 3107.v665000b_5109220.16.01.22.19
- 4.2.2
- 1aa43a5304405be7a7cb9cb5de7b97de9c4e8ddd3273e4dad00d6ae3eb39f0ef
+ 4.4.0
+ 5f228cb28f2edb97d8c3b667fb7b2fdcf06c46798e25ea889dad9e0b4bc2e2c1
@@ -88,6 +90,11 @@ THE SOFTWARE.
websocket-jetty10${project.version}
+
+ org.jenkins-ci.main
+ websocket-jetty12-ee8
+ ${project.version}
+ org.jenkins-ci.pluginsjunit
- 1280.v310a_78b_9a_1e0
+ 1284.vf75d778f98c5hpiorg.jenkins-ci.plugins.workflowworkflow-api
- 1322.v857eeeea_9902
+ 1332.vc21122317a_8ehpi
@@ -363,7 +372,7 @@ THE SOFTWARE.
io.jenkins.pluginssnakeyaml-api
- 2.2-111.vc6598e30cc65
+ 2.2-121.v5a_68b_9300b_d4hpi
@@ -423,14 +432,14 @@ THE SOFTWARE.
org.jenkins-ci.pluginscommand-launcher
- 107.v773860566e2e
+ 115.vd8b_301cc15d0hpiorg.jenkins-ci.pluginsjdk-tool
- 73.vddf737284550
+ 80.v8a_dee33ed6f0hpi
@@ -493,14 +502,14 @@ THE SOFTWARE.
io.jenkins.pluginscommons-lang3-api
- 3.14.0-76.vda_5591261cfe
+ 3.16.0-82.ve2b_07d659d95hpiio.jenkins.pluginscommons-text-api
- 1.12.0-119.v73ef73f2345d
+ 1.12.0-129.v99a_50df237f7hpi
@@ -627,14 +636,14 @@ THE SOFTWARE.
- org.eclipse.jetty
- jetty-maven-plugin
- 10.0.20
+ org.eclipse.jetty.ee8
+ jetty-ee8-maven-plugin
+ 12.0.12
- manual
+ 0${host}${port}
@@ -642,7 +651,9 @@ THE SOFTWARE.
default
- ${basedir}/src/realm.properties
+
+ ${basedir}/src/realm.properties
+
@@ -663,6 +674,12 @@ THE SOFTWARE.
truetrue
+
+ com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
diff --git a/war/src/main/js/add-item.js b/war/src/main/js/add-item.js
index 13ab6fb47b75..e648ecf8f3ff 100644
--- a/war/src/main/js/add-item.js
+++ b/war/src/main/js/add-item.js
@@ -145,8 +145,10 @@ $.when(getItems()).done(function (data) {
var iconDiv = drawIcon(elem);
item.appendChild(iconDiv);
+ var labelContainer = document.createElement("div");
+ item.appendChild(labelContainer);
- var label = item.appendChild(document.createElement("label"));
+ var label = labelContainer.appendChild(document.createElement("label"));
var radio = label.appendChild(document.createElement("input"));
radio.type = "radio";
@@ -158,7 +160,7 @@ $.when(getItems()).done(function (data) {
displayName.appendChild(document.createTextNode(elem.displayName));
- var desc = item.appendChild(document.createElement("div"));
+ var desc = labelContainer.appendChild(document.createElement("div"));
desc.className = "desc";
desc.innerHTML = checkForLink(elem.description);
diff --git a/war/src/main/js/widgets/add/addform.scss b/war/src/main/js/widgets/add/addform.scss
index ecd284250b1a..2610a3cec02f 100644
--- a/war/src/main/js/widgets/add/addform.scss
+++ b/war/src/main/js/widgets/add/addform.scss
@@ -59,16 +59,13 @@
}
.default-icon {
- position: absolute;
- left: 10px;
- top: 10px;
- height: 48px;
- width: 48px;
+ height: 40px;
+ width: 40px;
border-radius: 50%;
text-align: center;
- line-height: 48px;
+ line-height: 36px;
font-weight: bold;
- font-size: 175%;
+ font-size: 125%;
color: #ffffff;
text-shadow: rgba(0, 0, 0, 0.25) 0 -1px 1px;
opacity: 0.75;
diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js
index 110a09fa199f..442cf33269d3 100644
--- a/war/src/main/webapp/scripts/hudson-behavior.js
+++ b/war/src/main/webapp/scripts/hudson-behavior.js
@@ -903,57 +903,79 @@ function escapeHTML(html) {
}
/**
- * Wraps a