VYPR
Moderate severityNVD Advisory· Published Sep 21, 2022· Updated May 28, 2025

CVE-2022-41233

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.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:rundeckMaven
< 3.6.123.6.12

Affected products

2

Patches

1
032b3bb9eafe

Merge pull request #51 from jenkinsci/option-validate-artifact-acl

https://github.com/jenkinsci/rundeck-pluginLuis ToledoOct 28, 2022via ghsa
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

News mentions

1