CVE-2022-41233
Description
Jenkins Rundeck Plugin 3.6.11 and earlier lacks Run/Artifacts permission checks, allowing attackers with Item/Read to access build artifact information.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins Rundeck Plugin 3.6.11 and earlier lacks Run/Artifacts permission checks, allowing attackers with Item/Read to access build artifact information.
The Jenkins Rundeck Plugin, which integrates Jenkins with Rundeck for job execution and artifact management, fails to enforce the optional Run/Artifacts permission on multiple HTTP endpoints [1][3]. This permission is intended to restrict access to build artifacts, but due to missing checks, users with only Item/Read permission can bypass it.
An attacker with Item/Read permission can exploit these endpoints to obtain information about build artifacts from any job. The vulnerability requires no additional authentication beyond the basic read access [1]. The plugin's endpoints do not verify whether the user has the Run/Artifacts permission, even if it is enabled for the job.
The impact is information disclosure: attackers can learn details about build artifacts, which may include sensitive data such as configuration files, credentials, or proprietary code [3]. This could lead to further compromise if attackers leverage exposed information.
The issue is fixed in Rundeck Plugin version 3.6.12, released in September 2022 [4]. Users should upgrade to this version or later. The optional Run/Artifacts permission remains available and is properly enforced after the fix. The plugin source code is available on GitHub [2].
AI Insight generated on May 21, 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.plugins:rundeckMaven | < 3.6.12 | 3.6.12 |
Affected products
2- Range: unspecified
Patches
1032b3bb9eafeMerge pull request #51 from jenkinsci/option-validate-artifact-acl
3 files changed · +219 −1
pom.xml+24 −0 modified@@ -173,12 +173,36 @@ </exclusion> </exclusions> </dependency> + <!-- https://mvnrepository.com/artifact/cglib/cglib-nodep --> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <version>3.3.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib</artifactId> + <version>3.3.0</version> + <scope>test</scope> + </dependency> + + <!-- https://mvnrepository.com/artifact/org.objenesis/objenesis --> + <dependency> + <groupId>org.objenesis</groupId> + <artifactId>objenesis</artifactId> + <version>3.3</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.spockframework</groupId> <artifactId>spock-core</artifactId> <version>1.3-groovy-2.5</version> <scope>test</scope> </dependency> + <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId>
src/main/java/org/jenkinsci/plugins/rundeck/OptionProvider.java+23 −1 modified@@ -1,6 +1,6 @@ package org.jenkinsci.plugins.rundeck; -import hudson.model.AbstractProject; +import hudson.Functions; import hudson.model.Hudson; import hudson.model.Item; import hudson.model.ItemGroup; @@ -21,6 +21,8 @@ import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; +import static hudson.model.Run.ARTIFACTS; + /** * Option provider for Rundeck - see http://rundeck.org/docs/manual/jobs.html#option-model-provider * @@ -60,6 +62,13 @@ public void doArtifact(StaplerRequest request, StaplerResponse response) throws return; } + try { + this.checkArtifactPermissions(build); + }catch (Exception e){ + response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); + return; + } + List<Option> options = new ArrayList<OptionProvider.Option>(); for (Artifact artifact : build.getArtifacts()) { if (artifactPattern == null || artifactPattern.matcher(artifact.getFileName()).matches()) { @@ -148,6 +157,7 @@ public void doBuild(StaplerRequest request, StaplerResponse response) throws IOE RunList<?> builds = project.getBuilds(); for (Run<?, ?> build : builds) { Artifact artifact = findArtifact(artifactName, artifactPattern, build); + if (artifact != null) { String buildName = build.getDisplayName(); options.add(new Option(buildName, buildArtifactUrl(build, artifact))); @@ -248,6 +258,12 @@ private Artifact findArtifact(String artifactName, Pattern artifactPattern, Run< return null; } + try{ + this.checkArtifactPermissions(build); + }catch (Exception e){ + return null; + } + for (Artifact artifact : build.getArtifacts()) { if (StringUtils.equals(artifactName, artifact.getFileName())) { return artifact; @@ -290,6 +306,12 @@ private void writeJson(List<Option> options, StaplerResponse response) throws IO response.getWriter().append(json); } + private void checkArtifactPermissions(Run<?, ?> build){ + if(Functions.isArtifactsPermissionEnabled()){ + build.checkPermission(ARTIFACTS); + } + } + /** * Javabean representation of an option */
src/test/groovy/jenkinsci/plugins/rundeck/OptionProviderSpec.groovy+172 −0 added@@ -0,0 +1,172 @@ +package jenkinsci.plugins.rundeck + +import hudson.model.FreeStyleBuild +import hudson.model.Hudson +import hudson.model.Job +import hudson.model.Run +import hudson.security.AccessDeniedException2 +import hudson.util.RunList +import jenkins.model.Jenkins +import org.jenkinsci.plugins.rundeck.OptionProvider +import org.kohsuke.stapler.StaplerRequest +import org.kohsuke.stapler.StaplerResponse +import spock.lang.Specification + +import javax.servlet.http.HttpServletResponse + +import static hudson.model.Run.ARTIFACTS + +class OptionProviderSpec extends Specification { + + def originalHolder + + def setup() { + originalHolder = Jenkins.HOLDER + System.properties['hudson.security.ArtifactsPermission'] = "true" + } + + def cleanup() { + Jenkins.HOLDER = originalHolder + } + + def "test option artifact without permissions"(){ + + given: + + final Hudson jenkins = Mock() + jenkins.getInstanceOrNull() >> jenkins + jenkins.getInstance() >> jenkins + + def originalHolder = Jenkins.HOLDER + + Jenkins.HOLDER = new Jenkins.JenkinsHolder() { + Jenkins getInstance() { + return jenkins + } + } + + jenkins.getItemByFullName("test", _)>>Mock(Job) { + getLastSuccessfulBuild() >> Mock(FreeStyleBuild) { + checkPermission(ARTIFACTS) >> { throw new AccessDeniedException2(Jenkins.getAuthentication(), ARTIFACTS) } + } + } + + def response = Mock(StaplerResponse) + + when: + OptionProvider optionProvider = new OptionProvider() + def request = Mock(StaplerRequest){ + getParameter("project")>>"test" + getParameter("build")>>"lastSuccessful" + } + + optionProvider.doArtifact(request,response ) + + then: + 1 * response.sendError(HttpServletResponse.SC_BAD_REQUEST, {message-> message == "anonymous is missing the Run/Artifacts permission" }); + + + } + + def "test option artifact with permissions"(){ + + given: + + final Hudson jenkins = Mock(){ + getRootUrl()>>"http://localhost:8080" + } + + jenkins.getInstanceOrNull() >> jenkins + jenkins.getInstance() >> jenkins + + Jenkins.HOLDER = new Jenkins.JenkinsHolder() { + Jenkins getInstance() { + return jenkins + } + } + + jenkins.getItemByFullName("test", _)>>Mock(Job) { + getLastSuccessfulBuild() >> Mock(FreeStyleBuild) { + getArtifacts()>> [ + Mock(Run.Artifact){ + getFileName()>>"test-1.0.jar" + getHref()>>"artifact/test-1.0.jar" + } + ] + getUrl()>>"/rundeck/" + } + } + + def writer = Mock(PrintWriter) + def response = Mock(StaplerResponse){ + getWriter()>>writer + } + + when: + OptionProvider optionProvider = new OptionProvider() + def request = Mock(StaplerRequest){ + getParameter("project")>>"test" + getParameter("build")>>"lastSuccessful" + } + optionProvider.doArtifact(request,response ) + + then: + 1*writer.append({json-> json == "[{\"name\":\"test-1.0.jar\",\"value\":\"http://localhost:8080/rundeck/artifact/artifact/test-1.0.jar\"}]"}) + + } + + def "test option build without permissions"(){ + + given: + + final Hudson jenkins = Mock() + + jenkins.getInstanceOrNull() >> jenkins + jenkins.getInstance() >> jenkins + + Jenkins.HOLDER = new Jenkins.JenkinsHolder() { + Jenkins getInstance() { + return jenkins + } + } + + List builds = [ + Mock(FreeStyleBuild) { + getArtifacts()>> [ + Mock(Run.Artifact){ + getFileName()>>"test-1.0.jar" + getHref()>>"artifact/test-1.0.jar" + } + ] + getUrl()>>"/rundeck/" + checkPermission(ARTIFACTS) >> { throw new AccessDeniedException2(Jenkins.getAuthentication(), ARTIFACTS) } + + } + ] + + jenkins.getItemByFullName("test", _)>>Mock(Job) { + getBuilds()>> Mock(RunList){ + iterator()>>builds.iterator() + } + } + + def writer = Mock(PrintWriter) + def response = Mock(StaplerResponse){ + getWriter()>>writer + } + + when: + OptionProvider optionProvider = new OptionProvider() + def request = Mock(StaplerRequest){ + getParameter("project")>>"test" + getParameter("artifactRegex")>>".jar" + getParameter("build")>>"lastSuccessful" + } + def result = optionProvider.doBuild(request,response ) + + then: + 1*writer.append({json-> json == "[]"}) + + } + +} \ 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
4- github.com/advisories/GHSA-4jfq-4fqc-5j9cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-41233ghsaADVISORY
- github.com/jenkinsci/rundeck-plugin/commit/032b3bb9eafee5f83e3ddeb023eb34d0eeae19b7ghsaWEB
- www.jenkins.io/security/advisory/2022-09-21/ghsax_refsource_CONFIRMWEB
News mentions
1- Jenkins Security Advisory 2022-09-21Jenkins Security Advisories · Sep 21, 2022