VYPR
High severityNVD Advisory· Published Jan 22, 2019· Updated Aug 5, 2024

CVE-2019-1003001

CVE-2019-1003001

Description

A sandbox bypass vulnerability exists in Pipeline: Groovy Plugin 2.61 and earlier in src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowDefinition.java, src/main/java/org/jenkinsci/plugins/workflow/cps/CpsGroovyShellFactory.java that allows attackers with Overall/Read permission to provide a pipeline script to an HTTP endpoint that can result in arbitrary code execution on the Jenkins master JVM.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Pipeline: Groovy Plugin before 2.62 allows attackers with Overall/Read permission to bypass the sandbox and execute arbitrary code on the Jenkins master.

Vulnerability

A sandbox bypass vulnerability exists in the Jenkins Pipeline: Groovy Plugin version 2.61 and earlier [3]. The flaw is located in src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowDefinition.java and src/main/java/org/jenkinsci/plugins/workflow/cps/CpsGroovyShellFactory.java [1][3]. Attackers with Overall/Read permission can provide a malicious pipeline script to an HTTP endpoint, bypassing the Groovy sandbox and achieving arbitrary code execution on the Jenkins master JVM [3].

Exploitation

An attacker must possess Overall/Read permission on the Jenkins instance, which is a low privilege typically granted to users who can view jobs [1][3]. The attack involves submitting a crafted pipeline script to the appropriate HTTP endpoint (e.g., via the /createItem or similar API) that exploits the sandbox bypass to execute arbitrary Groovy code outside the sandbox restrictions [4]. Metasploit module exploit/multi/http/jenkins_metaprogramming provides a ready exploit for this vulnerability [4].

Impact

Successful exploitation allows an attacker to execute arbitrary Java/Groovy code on the Jenkins master JVM, effectively achieving remote code execution (RCE) [3]. The attacker can then perform any action that the Jenkins master can, including accessing credentials, modifying job configurations, reading secrets, and pivoting to internal network resources [1][3][4].

Mitigation

Jenkins released Pipeline: Groovy Plugin version 2.62 which fixes the sandbox bypass [1]. Users should upgrade to version 2.62 or later immediately [1]. Red Hat OpenShift Container Platform 3.11.82 includes the fix [1]. No effective workaround exists short of upgrading or removing the plugin. This CVE is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.

AI Insight generated on May 22, 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.workflow:workflow-cps-parentMaven
< 2.61.12.61.1
org.jenkins-ci.plugins:pipeline-model-definitionMaven
< 1.3.4.11.3.4.1
org.jenkins-ci.plugins:script-securityMaven
< 1.501.50

Affected products

4

Patches

3
6d7884dec610

[SECURITY-1266] Don't execute AST transforms in validate/translate

4 files changed · +85 8
  • pipeline-model-definition/src/main/groovy/org/jenkinsci/plugins/pipeline/modeldefinition/parser/Converter.groovy+7 6 modified
    @@ -42,14 +42,15 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ASTSchema
     import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTPipelineDef
     import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStep
     import org.jenkinsci.plugins.pipeline.modeldefinition.validator.DeclarativeValidatorContributor
    +import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox
     import org.jenkinsci.plugins.workflow.cps.CpsThread
     import org.jenkinsci.plugins.workflow.cps.GroovyShellDecorator
     
     import java.security.CodeSource
     import java.security.cert.Certificate
     
     import static groovy.lang.GroovyShell.DEFAULT_CODE_BASE
    -import static org.codehaus.groovy.control.Phases.CANONICALIZATION
    +import static org.codehaus.groovy.control.Phases.CONVERSION
     
     /**
      * Utilities for converting from/to {@link ModelASTPipelineDef} and raw Pipeline script.
    @@ -133,7 +134,7 @@ class Converter {
         }
     
         private static CompilerConfiguration makeCompilerConfiguration() {
    -        CompilerConfiguration cc = new CompilerConfiguration()
    +        CompilerConfiguration cc = GroovySandbox.createBaseCompilerConfiguration()
     
             ImportCustomizer ic = new ImportCustomizer()
             ic.addStarImports(NonCPS.class.getPackage().getName())
    @@ -166,9 +167,9 @@ class Converter {
                         model[0] = new ModelParser(source, enabledOptionalValidators).parse(true)
                     }
                 }
    -        }, CANONICALIZATION)
    +        }, CONVERSION)
     
    -        cu.compile(CANONICALIZATION)
    +        cu.compile(CONVERSION)
     
             return model[0]
         }
    @@ -195,9 +196,9 @@ class Converter {
                         model[0] = new ModelParser(source, enabledOptionalValidators).parsePlainSteps(source.AST)
                     }
                 }
    -        }, CANONICALIZATION)
    +        }, CONVERSION)
     
    -        cu.compile(CANONICALIZATION)
    +        cu.compile(CONVERSION)
     
             return model[0]
         }
    
  • pipeline-model-definition/src/test/java/org/jenkinsci/plugins/pipeline/modeldefinition/endpoints/ModelConverterActionTest.java+32 0 modified
    @@ -33,13 +33,15 @@
     import org.jenkinsci.plugins.pipeline.modeldefinition.model.BuildCondition;
     import org.jenkinsci.plugins.pipeline.modeldefinition.model.Tools;
     import org.junit.Test;
    +import org.jvnet.hudson.test.Issue;
     import org.jvnet.hudson.test.JenkinsRule;
     
     import java.net.URL;
     import java.util.Collections;
     
     import static org.junit.Assert.assertEquals;
     import static org.junit.Assert.assertNotNull;
    +import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
     
     public class ModelConverterActionTest extends AbstractModelDefTest {
    @@ -243,4 +245,34 @@ public void testInvalidScriptContentsInJson() throws Exception {
             assertTrue("Errors array (" + topErrors.toString(2) + ") didn't contain expected error '" + expectedError + "'",
                     foundExpectedErrorInJSON(topErrors, expectedError));
         }
    +
    +    @Issue("SECURITY-1266")
    +    @Test
    +    public void testLocalASTTransformInCompilation() throws Exception {
    +        final String rawJenkinsfile = fileContentsFromResources("localASTTransformInCompilation.groovy", true);
    +        // TODO: Get a better approach for skipping JSON-specific errors
    +        if (rawJenkinsfile != null) {
    +
    +            JenkinsRule.WebClient wc = j.createWebClient();
    +            WebRequest req = new WebRequest(new URL(wc.getContextPath() + ModelConverterAction.PIPELINE_CONVERTER_URL + "/validateJenkinsfile"), HttpMethod.POST);
    +
    +            assertNotNull(rawJenkinsfile);
    +
    +            NameValuePair pair = new NameValuePair("jenkinsfile", rawJenkinsfile);
    +            req.setRequestParameters(Collections.singletonList(pair));
    +
    +            String rawResult = wc.getPage(req).getWebResponse().getContentAsString();
    +            assertNotNull(rawResult);
    +
    +            JSONObject result = JSONObject.fromObject(rawResult);
    +            // TODO: Change this when we get proper JSON errors causing HTTP error codes
    +            assertEquals("Full result doesn't include status - " + result.toString(2), "ok", result.getString("status"));
    +            JSONObject resultData = result.getJSONObject("data");
    +            assertNotNull(resultData);
    +            assertEquals("Result wasn't a failure - " + result.toString(2), "failure", resultData.getString("result"));
    +
    +            assertNull(j.jenkins.getItem("should-not-exist"));
    +        }
    +    }
    +
     }
    
  • pipeline-model-definition/src/test/resources/localASTTransformInCompilation.groovy+44 0 added
    @@ -0,0 +1,44 @@
    +/*
    + * The MIT License
    + *
    + * Copyright (c) 2016, 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.
    + */
    +
    +import groovy.transform.*
    +import jenkins.model.Jenkins
    +import org.jenkinsci.plugins.workflow.job.WorkflowJob
    +
    +@ASTTest(value={ assert Jenkins.get().createProject(WorkflowJob.class, "should-not-exist") })
    +@Field int x
    +
    +pipeline {
    +    agent none
    +    stages {
    +        stage("foo") {
    +            steps {
    +                echo "hello"
    +            }
    +        }
    +    }
    +}
    +
    +
    +
    
  • pom.xml+2 2 modified
    @@ -85,12 +85,12 @@
           <dependency>
             <groupId>org.jenkins-ci.plugins.workflow</groupId>
             <artifactId>workflow-cps</artifactId>
    -        <version>2.58</version>
    +        <version>2.61.1</version>
           </dependency>
           <dependency>
             <groupId>org.jenkins-ci.plugins</groupId>
             <artifactId>script-security</artifactId>
    -        <version>1.46</version>
    +        <version>1.50</version>
           </dependency>
           <dependency>
             <groupId>org.jenkins-ci.plugins.workflow</groupId>
    
66c3e7aafe78

[SECURITY-1266] Don't execute AST transforms during syntax check or sandbox

6 files changed · +94 8
  • pom.xml+1 1 modified
    @@ -97,7 +97,7 @@
             <dependency>
                 <groupId>org.jenkins-ci.plugins</groupId>
                 <artifactId>script-security</artifactId>
    -            <version>1.48</version>
    +            <version>1.50</version>
             </dependency>
             <dependency>
                 <groupId>org.jenkins-ci.plugins</groupId>
    
  • src/main/java/org/jenkinsci/plugins/workflow/cps/CpsFlowDefinition.java+11 2 modified
    @@ -27,6 +27,7 @@
     import hudson.Extension;
     import hudson.model.Action;
     import hudson.model.Item;
    +import hudson.model.Job;
     import hudson.model.Queue;
     import hudson.model.Run;
     import hudson.model.TaskListener;
    @@ -39,11 +40,13 @@
     import org.jenkinsci.plugins.workflow.flow.FlowDurabilityHint;
     import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
     import org.jenkinsci.plugins.workflow.flow.GlobalDefaultFlowDurabilityLevel;
    +import org.kohsuke.stapler.AncestorInPath;
     import org.kohsuke.stapler.DataBoundConstructor;
     
     import java.io.IOException;
     import java.util.Arrays;
     import java.util.List;
    +
     import net.sf.json.JSON;
     import net.sf.json.JSONArray;
     import org.codehaus.groovy.control.CompilationFailedException;
    @@ -57,6 +60,7 @@
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.Stapler;
     import org.kohsuke.stapler.StaplerRequest;
    +import org.kohsuke.stapler.interceptor.RequirePOST;
     
     /**
      * @author Kohsuke Kawaguchi
    @@ -125,14 +129,19 @@ public String getDisplayName() {
                 return "Pipeline script";
             }
     
    +        @RequirePOST
             public FormValidation doCheckScript(@QueryParameter String value, @QueryParameter boolean sandbox) {
                 return sandbox ? FormValidation.ok() : ScriptApproval.get().checking(value, GroovyLanguage.get());
             }
     
    -        public JSON doCheckScriptCompile(@QueryParameter String value) {
    +        @RequirePOST
    +        public JSON doCheckScriptCompile(@AncestorInPath Item job, @QueryParameter String value) {
    +            if (!job.hasPermission(Job.CONFIGURE)) {
    +                return CpsFlowDefinitionValidator.CheckStatus.SUCCESS.asJSON();
    +            }
                 try {
                     CpsGroovyShell trusted = new CpsGroovyShellFactory(null).forTrusted().build();
    -                new CpsGroovyShellFactory(null).withParent(trusted).build().getClassLoader().parseClass(value);
    +                new CpsGroovyShellFactory(null).withParent(trusted).withSandbox(true).build().getClassLoader().parseClass(value);
                 } catch (CompilationFailedException x) {
                     return JSONArray.fromObject(CpsFlowDefinitionValidator.toCheckStatus(x).toArray());
                 }
    
  • src/main/java/org/jenkinsci/plugins/workflow/cps/CpsGroovyShellFactory.java+2 1 modified
    @@ -84,10 +84,11 @@ private CpsTransformer makeCpsTransformer() {
         }
     
         private CompilerConfiguration makeConfig() {
    -        CompilerConfiguration cc = new CompilerConfiguration();
    +        CompilerConfiguration cc = sandbox ? GroovySandbox.createBaseCompilerConfiguration() : new CompilerConfiguration();
     
             cc.addCompilationCustomizers(makeImportCustomizer());
             cc.addCompilationCustomizers(makeCpsTransformer());
    +
             cc.setScriptBaseClass(CpsScript.class.getName());
     
             for (GroovyShellDecorator d : decorators) {
    
  • src/main/java/org/jenkinsci/plugins/workflow/cps/replay/ReplayAction.java+5 2 modified
    @@ -73,6 +73,7 @@
     import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner;
     import org.kohsuke.accmod.Restricted;
     import org.kohsuke.accmod.restrictions.DoNotUse;
    +import org.kohsuke.stapler.AncestorInPath;
     import org.kohsuke.stapler.QueryParameter;
     import org.kohsuke.stapler.StaplerRequest;
     import org.kohsuke.stapler.StaplerResponse;
    @@ -346,12 +347,14 @@ private static String diff(String script, String oldText, String nueText) throws
         }
     
         // Stub, we do not need to do anything here.
    +    @RequirePOST
         public FormValidation doCheckScript() {
             return FormValidation.ok();
         }
     
    -    public JSON doCheckScriptCompile(@QueryParameter String value) {
    -        return Jenkins.getActiveInstance().getDescriptorByType(CpsFlowDefinition.DescriptorImpl.class).doCheckScriptCompile(value);
    +    @RequirePOST
    +    public JSON doCheckScriptCompile(@AncestorInPath Item job, @QueryParameter String value) {
    +        return Jenkins.get().getDescriptorByType(CpsFlowDefinition.DescriptorImpl.class).doCheckScriptCompile(job, value);
         }
     
         public static final Permission REPLAY = new Permission(Run.PERMISSIONS, "Replay", Messages._Replay_permission_description(), Item.CONFIGURE, PermissionScope.RUN);
    
  • src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowDefinition2Test.java+16 0 modified
    @@ -536,4 +536,20 @@ public void finalizer() throws Exception {
             jenkins.assertLogContains("Object.finalize()", b);
             jenkins.assertLogNotContains("Should never get here", b);
         }
    +
    +    @Issue("SECURITY-266")
    +    @Test
    +    public void sandboxRejectsASTTransforms() throws Exception {
    +        WorkflowJob p = jenkins.jenkins.createProject(WorkflowJob.class, "p");
    +        p.setDefinition(new CpsFlowDefinition("import groovy.transform.*\n" +
    +                "import jenkins.model.Jenkins\n" +
    +                "import org.jenkinsci.plugins.workflow.job.WorkflowJob\n" +
    +                "@ASTTest(value={ assert Jenkins.get().createProject(WorkflowJob.class, \"should-not-exist\") })\n" +
    +                "@Field int x\n" +
    +                "echo 'hello'\n", true));
    +        WorkflowRun b = jenkins.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0));
    +        jenkins.assertLogContains("Annotation ASTTest cannot be used in the sandbox", b);
    +
    +        assertNull(jenkins.jenkins.getItem("should-not-exist"));
    +    }
     }
    
  • src/test/java/org/jenkinsci/plugins/workflow/cps/CpsFlowDefinitionValidatorTest.java+59 2 modified
    @@ -25,19 +25,76 @@
     package org.jenkinsci.plugins.workflow.cps;
     
     import static org.hamcrest.Matchers.containsString;
    +
    +import hudson.model.Item;
    +import hudson.model.User;
    +import hudson.security.ACL;
    +import hudson.security.ACLContext;
    +import jenkins.model.Jenkins;
    +import org.jenkinsci.plugins.workflow.job.WorkflowJob;
     import org.junit.Test;
     import static org.junit.Assert.*;
     import org.junit.Rule;
    +import org.jvnet.hudson.test.Issue;
     import org.jvnet.hudson.test.JenkinsRule;
    +import org.jvnet.hudson.test.MockAuthorizationStrategy;
     
     public class CpsFlowDefinitionValidatorTest {
     
         @Rule public JenkinsRule r = new JenkinsRule();
     
         @Test public void doCheckScriptCompile() throws Exception {
             CpsFlowDefinition.DescriptorImpl d = r.jenkins.getDescriptorByType(CpsFlowDefinition.DescriptorImpl.class);
    -        assertThat(d.doCheckScriptCompile("echo 'hello'").toString(), containsString("\"success\""));
    -        assertThat(d.doCheckScriptCompile("echo 'hello").toString(), containsString("\"fail\""));
    +        WorkflowJob job = r.jenkins.createProject(WorkflowJob.class, "w");
    +        assertThat(d.doCheckScriptCompile(job, "echo 'hello'").toString(), containsString("\"success\""));
    +        assertThat(d.doCheckScriptCompile(job, "echo 'hello").toString(), containsString("\"fail\""));
    +    }
    +
    +    @Issue("SECURITY-1266")
    +    @Test
    +    public void blockASTTest() throws Exception {
    +        CpsFlowDefinition.DescriptorImpl d = r.jenkins.getDescriptorByType(CpsFlowDefinition.DescriptorImpl.class);
    +        WorkflowJob job = r.jenkins.createProject(WorkflowJob.class, "w");
    +        assertThat(d.doCheckScriptCompile(job, "import groovy.transform.*\n" +
    +                "import jenkins.model.Jenkins\n" +
    +                "import org.jenkinsci.plugins.workflow.job.WorkflowJob\n" +
    +                "@ASTTest(value={ assert Jenkins.get().createProject(WorkflowJob.class, \"should-not-exist\") })\n" +
    +                "@Field int x\n" +
    +                "echo 'hello'\n").toString(), containsString("Annotation ASTTest cannot be used in the sandbox"));
    +
    +        assertNull(r.jenkins.getItem("should-not-exist"));
    +    }
    +
    +    @Issue("SECURITY-1266")
    +    @Test
    +    public void blockGrab() throws Exception {
    +        CpsFlowDefinition.DescriptorImpl d = r.jenkins.getDescriptorByType(CpsFlowDefinition.DescriptorImpl.class);
    +        WorkflowJob job = r.jenkins.createProject(WorkflowJob.class, "w");
    +        assertThat(d.doCheckScriptCompile(job, "@Grab(group='foo', module='bar', version='1.0')\n" +
    +                "def foo\n").toString(), containsString("Annotation Grab cannot be used in the sandbox"));
         }
     
    +    @Issue("SECURITY-1266")
    +    @Test
    +    public void configureRequired() throws Exception {
    +        CpsFlowDefinition.DescriptorImpl d = r.jenkins.getDescriptorByType(CpsFlowDefinition.DescriptorImpl.class);
    +
    +        r.jenkins.setSecurityRealm(r.createDummySecurityRealm());
    +        // Set up an administrator, and three developer users with varying levels of access.
    +        r.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().
    +                grant(Jenkins.ADMINISTER).everywhere().to("admin").
    +                grant(Jenkins.READ, Item.CONFIGURE).everywhere().to("dev1").
    +                grant(Jenkins.READ).everywhere().to("dev2"));
    +        WorkflowJob job = r.jenkins.createProject(WorkflowJob.class, "w");
    +
    +        try (ACLContext context = ACL.as(User.getById("admin", true))) {
    +            assertThat(d.doCheckScriptCompile(job, "echo 'hello").toString(), containsString("fail"));
    +        }
    +        try (ACLContext context = ACL.as(User.getById("dev1", true))) {
    +            assertThat(d.doCheckScriptCompile(job, "echo 'hello").toString(), containsString("fail"));
    +        }
    +        try (ACLContext context = ACL.as(User.getById("dev2", true))) {
    +            assertThat(d.doCheckScriptCompile(job, "echo 'hello").toString(), containsString("success"));
    +        }
    +    }
     }
    
2c5122e50742

[SECURITY-1266] Block problematic AST transforms from sandbox

3 files changed · +124 1
  • src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/GroovySandbox.java+14 1 modified
    @@ -25,9 +25,12 @@
     package org.jenkinsci.plugins.scriptsecurity.sandbox.groovy;
     
     import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    +import groovy.grape.GrabAnnotationTransformation;
     import groovy.lang.GroovyShell;
     import groovy.lang.Script;
     
    +import java.util.Collections;
    +import java.util.HashSet;
     import java.util.concurrent.Callable;
     import javax.annotation.Nonnull;
     import org.codehaus.groovy.control.CompilerConfiguration;
    @@ -58,11 +61,21 @@ public class GroovySandbox {
          * @return a compiler configuration set up to use the sandbox
          */
         public static @Nonnull CompilerConfiguration createSecureCompilerConfiguration() {
    -        CompilerConfiguration cc = new CompilerConfiguration();
    +        CompilerConfiguration cc = createBaseCompilerConfiguration();
             cc.addCompilationCustomizers(new SandboxTransformer());
             return cc;
         }
     
    +    /**
    +     * Prepares a compiler configuration that rejects certain AST transformations. Used by {@link #createSecureCompilerConfiguration()}.
    +     */
    +    public static @Nonnull CompilerConfiguration createBaseCompilerConfiguration() {
    +        CompilerConfiguration cc = new CompilerConfiguration();
    +        cc.addCompilationCustomizers(new RejectASTTransformsCustomizer());
    +        cc.setDisabledGlobalASTTransformations(new HashSet<>(Collections.singletonList(GrabAnnotationTransformation.class.getName())));
    +        return cc;
    +    }
    +
         /**
          * Prepares a classloader for Groovy shell for sandboxing.
          *
    
  • src/main/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/RejectASTTransformsCustomizer.java+83 0 added
    @@ -0,0 +1,83 @@
    +/*
    + * The MIT License
    + *
    + * Copyright (c) 2018, 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 org.jenkinsci.plugins.scriptsecurity.sandbox.groovy;
    +
    +import com.google.common.collect.ImmutableList;
    +import groovy.lang.Grab;
    +import groovy.transform.ASTTest;
    +import org.codehaus.groovy.ast.AnnotatedNode;
    +import org.codehaus.groovy.ast.AnnotationNode;
    +import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
    +import org.codehaus.groovy.ast.ClassNode;
    +import org.codehaus.groovy.classgen.GeneratorContext;
    +import org.codehaus.groovy.control.CompilationFailedException;
    +import org.codehaus.groovy.control.CompilePhase;
    +import org.codehaus.groovy.control.SourceUnit;
    +import org.codehaus.groovy.control.customizers.CompilationCustomizer;
    +
    +import java.lang.annotation.Annotation;
    +import java.util.List;
    +
    +public class RejectASTTransformsCustomizer extends CompilationCustomizer {
    +    private static final List<Class<? extends Annotation>> BLOCKED_TRANSFORMS = ImmutableList.of(ASTTest.class, Grab.class);
    +
    +    public RejectASTTransformsCustomizer() {
    +        super(CompilePhase.CONVERSION);
    +    }
    +
    +    @Override
    +    public void call(final SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
    +        new RejectASTTransformsVisitor(source).visitClass(classNode);
    +    }
    +
    +    private static class RejectASTTransformsVisitor extends ClassCodeVisitorSupport {
    +        private SourceUnit source;
    +
    +        public RejectASTTransformsVisitor(SourceUnit source) {
    +            this.source = source;
    +        }
    +
    +        @Override
    +        protected SourceUnit getSourceUnit() {
    +            return source;
    +        }
    +
    +        /**
    +         * If the node is annotated with one of the blocked transform annotations, throw a security exception.
    +         *
    +         * @param node the node to process
    +         */
    +        @Override
    +        public void visitAnnotations(AnnotatedNode node) {
    +            for (AnnotationNode an : node.getAnnotations()) {
    +                for (Class<? extends Annotation> blockedAnnotation : BLOCKED_TRANSFORMS) {
    +                    if (blockedAnnotation.getSimpleName().equals(an.getClassNode().getName())) {
    +                        throw new SecurityException("Annotation " + blockedAnnotation.getSimpleName() + " cannot be used in the sandbox.");
    +                    }
    +                }
    +            }
    +        }
    +    }
    +}
    
  • src/test/java/org/jenkinsci/plugins/scriptsecurity/sandbox/groovy/SandboxInterceptorTest.java+27 0 modified
    @@ -74,12 +74,15 @@
     import org.junit.Rule;
     import org.junit.Test;
     import org.junit.rules.ErrorCollector;
    +import org.junit.rules.ExpectedException;
     import org.jvnet.hudson.test.Issue;
     
     public class SandboxInterceptorTest {
     
         @Rule public ErrorCollector errors = new ErrorCollector();
     
    +    @Rule public ExpectedException thrown = ExpectedException.none();
    +
         @Test public void genericWhitelist() throws Exception {
             assertEvaluate(new GenericWhitelist(), 3, "'foo bar baz'.split(' ').length");
             assertEvaluate(new GenericWhitelist(), false, "def x = null; x != null");
    @@ -818,6 +821,30 @@ public void explode() {}
             assertRejected(new StaticWhitelist(), "staticMethod hudson.model.Hudson getInstance", "hudson.model.Hudson.instance");
         }
     
    +    @Issue("SECURITY-1266")
    +    @Test
    +    public void blockedASTTransformsASTTest() throws Exception {
    +        GroovyShell shell = new GroovyShell(GroovySandbox.createSecureCompilerConfiguration());
    +
    +        thrown.expect(MultipleCompilationErrorsException.class);
    +        thrown.expectMessage("Annotation ASTTest cannot be used in the sandbox");
    +
    +        shell.parse("import groovy.transform.*\n" +
    +                "@ASTTest(value={ assert true })\n" +
    +                "@Field int x\n");
    +    }
    +
    +    @Issue("SECURITY-1266")
    +    @Test
    +    public void blockedASTTransformsGrab() throws Exception {
    +        GroovyShell shell = new GroovyShell(GroovySandbox.createSecureCompilerConfiguration());
    +        thrown.expect(MultipleCompilationErrorsException.class);
    +        thrown.expectMessage("Annotation Grab cannot be used in the sandbox");
    +
    +        shell.parse("@Grab(group='foo', module='bar', version='1.0')\n" +
    +                "def foo\n");
    +    }
    +
         private static Object evaluate(Whitelist whitelist, String script) {
             GroovyShell shell = new GroovyShell(GroovySandbox.createSecureCompilerConfiguration());
             Object actual = GroovySandbox.run(shell.parse(script), whitelist);
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

12

News mentions

0

No linked articles in our index yet.