CVE-2026-27100
Description
Jenkins 2.550 and earlier, LTS 2.541.1 and earlier accepts Run Parameter values that refer to builds the user submitting the build does not have access to, allowing attackers with Item/Build and Item/Configure permission to obtain information about the existence of jobs, the existence of builds, and if a specified build exists, its display name.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins builds prior to 2.551 and LTS 2.541.2 leak information about job and build existence through Run Parameters, enabling enumeration by attackers with Item/Build and Item/Configure permissions.
Vulnerability
CVE-2026-27100 is an information disclosure vulnerability in Jenkins core, affecting weekly releases up to and including 2.550 and LTS releases up to and including 2.541.1 [1][2][4]. The root cause is that Jenkins accepts Run Parameter values that refer to builds the submitting user does not have access to. This allows an attacker to infer the existence and display name of builds in jobs they cannot normally view.
Exploitation
To exploit this vulnerability, an attacker needs Item/Build and Item/Configure permissions [2][4]. By crafting a job with a Run Parameter and supplying an arbitrary build number or name, the attacker can observe whether the system accepts the value (indicating the build exists) or rejects it. The attack is carried out through the web interface, where the parameter is evaluated during job triggering [1][4].
Impact
Successful exploitation enables an attacker to enumerate which builds exist, determine the existence of specific jobs, and – if a build exists – learn its display name [2][4]. This information leak can assist in mapping out internal system state and identifying sensitive build artifacts, though the vulnerability does not grant code execution or direct unauthorized access to build content.
Mitigation
Jenkins has addressed this issue in versions 2.551 (weekly) and 2.541.2 (LTS) by rejecting Run Parameter values that refer to builds the user does not have permission to access [1][4]. All prior versions are considered affected. Users should upgrade to these fixed versions immediately [2][4]. No workarounds are mentioned in the advisory.
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.main:jenkins-coreMaven | >= 2.542, < 2.551 | 2.551 |
org.jenkins-ci.main:jenkins-coreMaven | < 2.541.2 | 2.541.2 |
Affected products
2- Range: <=2.550, LTS <=2.541.1
- Jenkins Project/Jenkinsv5Range: 2.551
Patches
15 files changed · +248 −3
core/src/main/java/hudson/model/RunParameterValue.java+14 −2 modified@@ -28,11 +28,13 @@ import hudson.EnvVars; import java.util.Locale; import jenkins.model.Jenkins; +import jenkins.util.SystemProperties; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.export.Exported; public class RunParameterValue extends ParameterValue { + private static /* non-final for Script console */ boolean SKIP_EXISTENCE_CHECK = SystemProperties.getBoolean(RunParameterValue.class.getName() + ".SKIP_EXISTENCE_CHECK", false); private final String runId; @DataBoundConstructor @@ -49,9 +51,19 @@ public RunParameterValue(String name, String runId) { private static String check(String runId) { if (runId == null || runId.indexOf('#') == -1) { throw new IllegalArgumentException(runId); - } else { - return runId; } + + if (!SKIP_EXISTENCE_CHECK) { + try { + if (Run.fromExternalizableId(runId) == null) { + throw new IllegalArgumentException(runId); + } + } catch (RuntimeException e) { + throw new IllegalArgumentException(runId); + } + } + + return runId; } /**
test/src/test/java/hudson/model/RunParameterValueTest.java+11 −1 renamed@@ -29,12 +29,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.junit.jupiter.api.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockFolder; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; +@WithJenkins class RunParameterValueTest { @SuppressWarnings("ResultOfObjectAllocationIgnored") @Test - void robustness() { + void robustness(JenkinsRule jenkinsRule) throws Exception { + // Prepare the job for the parameter value + final MockFolder folder = jenkinsRule.createFolder("folder"); + final FreeStyleProject job = folder.createProject(FreeStyleProject.class, "job"); + job.updateNextBuildNumber(57); + jenkinsRule.assertBuildStatusSuccess(job.scheduleBuild2(0)); + RunParameterValue rpv = new RunParameterValue("whatever", "folder/job#57"); assertEquals("whatever", rpv.getName()); assertEquals("folder/job", rpv.getJobName());
test/src/test/java/jenkins/security/Security3658Test.java+177 −0 added@@ -0,0 +1,177 @@ +package jenkins.security; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; + +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.BuildListener; +import hudson.model.FreeStyleBuild; +import hudson.model.FreeStyleProject; +import hudson.model.Item; +import hudson.model.ParameterValue; +import hudson.model.ParametersAction; +import hudson.model.ParametersDefinitionProperty; +import hudson.model.RunParameterDefinition; +import hudson.model.RunParameterValue; +import hudson.tasks.Builder; +import java.lang.reflect.Field; +import java.util.Map; +import jenkins.model.Jenkins; +import org.htmlunit.Page; +import org.htmlunit.WebResponse; +import org.htmlunit.html.HtmlFormUtil; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.html.HtmlSelect; +import org.junit.jupiter.api.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; +import org.jvnet.hudson.test.recipes.LocalData; +import org.w3c.dom.Element; + +@WithJenkins +public class Security3658Test { + + @Test + void testNormalCase(JenkinsRule jenkinsRule) throws Exception { + final Jenkins j = jenkinsRule.jenkins; + final FreeStyleProject referencedProject = jenkinsRule.createFreeStyleProject(); + final FreeStyleBuild build = jenkinsRule.buildAndAssertSuccess(referencedProject); + build.setDisplayName("Top Secret Build"); + build.save(); + final FreeStyleProject referencingProject = jenkinsRule.createFreeStyleProject(); + referencingProject.addProperty(new ParametersDefinitionProperty(new RunParameterDefinition("the_run", referencedProject.getName(), null, null))); + referencingProject.getBuildersList().add(new EnvVarsPrinterBuilder()); + referencingProject.save(); + j.setSecurityRealm(jenkinsRule.createDummySecurityRealm()); + j.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("alice")); + + try (JenkinsRule.WebClient wc = jenkinsRule.createWebClient().login("alice").withThrowExceptionOnFailingStatusCode(false)) { + // Navigate to the build page + final HtmlPage page = wc.getPage(referencingProject, "build?delay=0sec"); + // Submit and assert success + final Page submission = HtmlFormUtil.submit(page.getFormByName("parameters")); + final WebResponse response = submission.getWebResponse(); + assertThat(response.getStatusCode(), equalTo(200)); + } + jenkinsRule.waitUntilNoActivityUpTo(1_000); + // The build log contains the build name + assertThat(referencingProject.getBuildByNumber(1).getLog(1000), hasItem(containsString("the_run_NAME=Top Secret Build"))); + } + + @Test + void security3658(JenkinsRule jenkinsRule) throws Exception { + final Jenkins j = jenkinsRule.jenkins; + final FreeStyleProject referencedProject = jenkinsRule.createFreeStyleProject(); + final FreeStyleBuild build = jenkinsRule.buildAndAssertSuccess(referencedProject); + build.setDisplayName("Top Secret Build"); + build.save(); + final FreeStyleProject referencingProject = jenkinsRule.createFreeStyleProject(); + referencingProject.addProperty(new ParametersDefinitionProperty(new RunParameterDefinition("the_run", referencedProject.getName(), null, null))); + referencingProject.save(); + j.setSecurityRealm(jenkinsRule.createDummySecurityRealm()); + j.setAuthorizationStrategy(new MockAuthorizationStrategy() + .grant(Jenkins.ADMINISTER).everywhere().to("alice") + .grant(Jenkins.READ).everywhere().toEveryone() + .grant(Item.READ, Item.BUILD).onItems(referencingProject).to("bob")); + + try (JenkinsRule.WebClient wc = jenkinsRule.createWebClient().login("bob").withThrowExceptionOnFailingStatusCode(false)) { + // Navigate to the build page and manipulate the DOM to specify an inaccessible build + final HtmlPage page = wc.getPage(referencingProject, "build?delay=0sec"); + final Element option = page.createElement("option"); + option.setTextContent(referencedProject.getName() + "#1"); + page.getElementByName("runId").appendChild(option); + ((HtmlSelect) page.getElementByName("runId")).setSelectedIndex(0); + + // Submit and assert HTTP error + final Page submission = HtmlFormUtil.submit(page.getFormByName("parameters")); + final WebResponse response = submission.getWebResponse(); + assertThat(response.getStatusCode(), equalTo(500)); + assertThat(response.getContentAsString(), allOf( + containsString("java.lang.IllegalArgumentException: " + referencedProject.getName() + "#1"), + containsString("Caused: java.lang.IllegalArgumentException: Failed to instantiate class hudson.model.RunParameterValue from {\"name\":\"the_run\",\"runId\":\"" + referencedProject.getName() + "#1\"}"))); + } + jenkinsRule.waitUntilNoActivityUpTo(1_000); + assertThat(referencingProject.getBuilds(), is(empty())); + } + + @Test + void escapeHatch(JenkinsRule jenkinsRule) throws Exception { + final Jenkins j = jenkinsRule.jenkins; + final FreeStyleProject referencedProject = jenkinsRule.createFreeStyleProject(); + final FreeStyleBuild build = jenkinsRule.buildAndAssertSuccess(referencedProject); + build.setDisplayName("Top Secret Build"); + build.save(); + final FreeStyleProject referencingProject = jenkinsRule.createFreeStyleProject(); + referencingProject.addProperty(new ParametersDefinitionProperty(new RunParameterDefinition("the_run", referencedProject.getName(), null, null))); + referencingProject.getBuildersList().add(new EnvVarsPrinterBuilder()); + referencingProject.save(); + j.setSecurityRealm(jenkinsRule.createDummySecurityRealm()); + j.setAuthorizationStrategy(new MockAuthorizationStrategy() + .grant(Jenkins.ADMINISTER).everywhere().to("alice") + .grant(Jenkins.READ).everywhere().toEveryone() + .grant(Item.READ, Item.BUILD).onItems(referencingProject).to("bob")); + + final Field escapeHatch = RunParameterValue.class.getDeclaredField("SKIP_EXISTENCE_CHECK"); + escapeHatch.setAccessible(true); + escapeHatch.set(null, true); + try { + try (JenkinsRule.WebClient wc = jenkinsRule.createWebClient().login("bob").withThrowExceptionOnFailingStatusCode(false)) { + // Navigate to the build page and manipulate the DOM to specify an inaccessible build + final HtmlPage page = wc.getPage(referencingProject, "build?delay=0sec"); + final Element option = page.createElement("option"); + option.setTextContent(referencedProject.getName() + "#1"); + page.getElementByName("runId").appendChild(option); + ((HtmlSelect) page.getElementByName("runId")).setSelectedIndex(0); + + final Page submission = HtmlFormUtil.submit(page.getFormByName("parameters")); + final WebResponse response = submission.getWebResponse(); + assertThat(response.getStatusCode(), not(equalTo(500))); + assertThat(response.getContentAsString(), allOf( + not(containsString("java.lang.IllegalArgumentException: " + referencedProject.getName() + "#1")), + not(containsString("Caused: java.lang.IllegalArgumentException: Failed to instantiate class hudson.model.RunParameterValue from {\"name\":\"the_run\",\"runId\":\"" + referencedProject.getName() + "#1\"}")))); + } + jenkinsRule.waitUntilNoActivityUpTo(1_000); + // The build log contains the build name + assertThat(referencingProject.getBuildByNumber(1).getLog(1000), hasItem(containsString("the_run_NAME=Top Secret Build"))); + } finally { + escapeHatch.set(null, false); + } + } + + @Test + @LocalData + void historicalRecords(JenkinsRule jenkinsRule) throws Exception { + // Ensure that we can still read old RunParameterValue records that reference non-existent builds + final ParameterValue parameter = jenkinsRule.jenkins.getItemByFullName("fs", FreeStyleProject.class).getBuildByNumber(1).getAction(ParametersAction.class).getParameter("copy"); + assertThat(parameter, instanceOf(RunParameterValue.class)); + final RunParameterValue runParameterValue = (RunParameterValue) parameter; + assertThat(runParameterValue.getRunId(), is("nonexistent#1")); + assertThat(runParameterValue.getJobName(), is("nonexistent")); + assertThat(runParameterValue.getNumber(), is("1")); + assertThat(runParameterValue.getRun(), is(nullValue())); + } + + public static class EnvVarsPrinterBuilder extends Builder { + @Override + public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) { + try { + for (Map.Entry<String, String> e : build.getEnvironment(listener).entrySet()) { + listener.getLogger().println(e.getKey() + "=" + e.getValue()); + } + } catch (Exception e) { + listener.getLogger().println("Failed to get environment: " + e); + } + return true; + } + } +}
test/src/test/resources/jenkins/security/Security3658Test/historicalRecords/jobs/fs/builds/1/build.xml+29 −0 added@@ -0,0 +1,29 @@ +<?xml version='1.1' encoding='UTF-8'?> +<build> + <actions> + <hudson.model.ParametersAction> + <safeParameters class="sorted-set"/> + <parameters> + <hudson.model.RunParameterValue> + <name>copy</name> + <runId>nonexistent#1</runId> + </hudson.model.RunParameterValue> + </parameters> + <parameterDefinitionNames> + <string>copy</string> + </parameterDefinitionNames> + </hudson.model.ParametersAction> + </actions> + <queueId>1</queueId> + <timestamp>1769414406434</timestamp> + <startTime>1769414406461</startTime> + <result>SUCCESS</result> + <duration>81</duration> + <charset>UTF-8</charset> + <keepLog>false</keepLog> + <builtOn></builtOn> + <scm class="hudson.scm.NullChangeLogParser"/> + <culprits class="java.util.Collections$UnmodifiableSet"> + <c class="sorted-set"/> + </culprits> +</build> \ No newline at end of file
test/src/test/resources/jenkins/security/Security3658Test/historicalRecords/jobs/fs/config.xml+17 −0 added@@ -0,0 +1,17 @@ +<?xml version='1.1' encoding='UTF-8'?> +<project> + <actions/> + <description></description> + <keepDependencies>false</keepDependencies> + <properties/> + <scm class="hudson.scm.NullSCM"/> + <canRoam>true</canRoam> + <disabled>false</disabled> + <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding> + <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding> + <triggers/> + <concurrentBuild>false</concurrentBuild> + <builders/> + <publishers/> + <buildWrappers/> +</project> \ No newline at end of file
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-wfhp-qgm8-5p5cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-27100ghsaADVISORY
- www.jenkins.io/security/advisory/2026-02-18/ghsavendor-advisoryWEB
- github.com/jenkinsci/jenkins/commit/f92eadb5813f04ca27439455e2573c3171e93a45ghsaWEB
- github.com/jenkinsci/jenkins/releases/tag/jenkins-2.541.2ghsaWEB
- github.com/jenkinsci/jenkins/releases/tag/jenkins-2.551ghsaWEB
News mentions
1- Jenkins Security Advisory 2026-02-18Jenkins Security Advisories · Feb 18, 2026