Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-69899

@Field annotated fields cause exceptions in sandboxed Groovy scripts (including Pipelines)

    XMLWordPrintable

Details

    • Bug
    • Status: Resolved (View Workflow)
    • Major
    • Resolution: Fixed
    • script-security-plugin
    • Jenkins - 2.361.2
      Pipeline: Groovy - 2803.v1a_f77ffcc773
      Pipeline: Groovy Libraries - 613.v9c41a_160233f
      Script Security - 1184.v85d16b_d851b_3
    • script-security 1189.vb_a_b_7c8fd5fde

    Description

      After updating Script Security Plugin to version 1184.v85d16b_d851b_3, sandboxed scripts including Pipelines fail with an exception if they contain @Field annotated fields, for example:

      @Field String foo 

      The exception looks like the following:

      General error during class generation: Index -1 out of bounds for length 0
      
      java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 0
      	at java.base/jdk.internal.util.Preconditions.outOfBounds(Unknown Source)
      	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Unknown Source)
      	at java.base/jdk.internal.util.Preconditions.checkIndex(Unknown Source)
      	at java.base/java.util.Objects.checkIndex(Unknown Source)
      	at java.base/java.util.ArrayList.remove(Unknown Source)
      	at org.codehaus.groovy.classgen.asm.OperandStack.popWithMessage(OperandStack.java:124)
      	at org.codehaus.groovy.classgen.asm.OperandStack.remove(OperandStack.java:265)
      	at org.codehaus.groovy.classgen.asm.OperandStack.replace(OperandStack.java:332)
      	at org.codehaus.groovy.classgen.asm.CallSiteWriter.makeCallSite(CallSiteWriter.java:355)
      	at org.codehaus.groovy.classgen.asm.InvocationWriter.makeCachedCall(InvocationWriter.java:340)
      	at org.codehaus.groovy.classgen.asm.InvocationWriter.makeCall(InvocationWriter.java:430)
      	at org.codehaus.groovy.classgen.asm.InvocationWriter.makeCall(InvocationWriter.java:137)
      	at org.codehaus.groovy.classgen.asm.InvocationWriter.writeInvokeStaticMethod(InvocationWriter.java:548)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitStaticMethodCallExpression(AsmClassGenerator.java:850)
      	at org.codehaus.groovy.ast.expr.StaticMethodCallExpression.visit(StaticMethodCallExpression.java:45)
      	at org.codehaus.groovy.classgen.asm.BinaryExpressionHelper.evaluateEqual(BinaryExpressionHelper.java:361)
      	at org.codehaus.groovy.classgen.asm.BinaryExpressionHelper.eval(BinaryExpressionHelper.java:129)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitBinaryExpression(AsmClassGenerator.java:685)
      	at org.codehaus.groovy.ast.expr.BinaryExpression.visit(BinaryExpression.java:51)
      	at org.codehaus.groovy.classgen.asm.StatementWriter.writeExpressionStatement(StatementWriter.java:628)
      	at org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.writeExpressionStatement(OptimizingStatementWriter.java:411)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitExpressionStatement(AsmClassGenerator.java:667)
      	at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42)
      	at org.codehaus.groovy.classgen.asm.StatementWriter.writeBlockStatement(StatementWriter.java:93)
      	at org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.writeBlockStatement(OptimizingStatementWriter.java:205)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitBlockStatement(AsmClassGenerator.java:613)
      	at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:71)
      	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:104)
      	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:115)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitStdMethod(AsmClassGenerator.java:477)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructorOrMethod(AsmClassGenerator.java:413)
      	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructor(ClassCodeVisitorSupport.java:122)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitConstructor(AsmClassGenerator.java:549)
      	at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1077)
      	at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:53)
      	at org.codehaus.groovy.classgen.AsmClassGenerator.visitClass(AsmClassGenerator.java:259)
      	at org.codehaus.groovy.control.CompilationUnit$17.call(CompilationUnit.java:848)
      	at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1087)
      	at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
      	at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
      	at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
      	at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
      	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
      	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
      	at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:142)
      	at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
      	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:561)
      	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:513)
      	at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
      	at hudson.model.ResourceController.execute(ResourceController.java:107)
      	at hudson.model.Executor.run(Executor.java:449)
      
      1 error
      
      	at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309)
      	at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1107)
      	at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
      	at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
      	at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
      	at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
      	at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
      	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
      	at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:142)
      	at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:127)
      	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:561)
      	at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:513)
      	at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:335)
      	at hudson.model.ResourceController.execute(ResourceController.java:107)
      	at hudson.model.Executor.run(Executor.java:449)
      

      Workarounds

      Provide an initial value:

      @Field String foo = null

      Alternatively, use Object as a type:

      @Field Object foo
      @Field def bar 

      Attachments

        Activity

          Sorry for not replying earlier, I was trying to trim down our pipeline script, because I cannot share it here, but mulgish provided a good example. It seems we are facing the issue as described by dnusbaum and the provided workaround was successful. In our case, we have something like this in our script:

          @Field ClassFromSharedGroovyLibrary instance
          

          Assigning it to null solved the issue.

          andreyakostovsap Andreya Kostov added a comment - Sorry for not replying earlier, I was trying to trim down our pipeline script, because I cannot share it here, but mulgish provided a good example. It seems we are facing the issue as described by  dnusbaum and the provided workaround was successful. In our case, we have something like this in our script: @Field ClassFromSharedGroovyLibrary instance Assigning it to null solved the issue.
          mulgish Juri Duval added a comment -

          dnusbaum DOCKER_COMPOSE.groovy contains below code:

          package core.cd
          
          import groovy.transform.Field
          
          @Field String IMAGE
          @Field String REGISTRY
          @Field String STACK
          
          void prepareDeployment() {
              timeout(5) {
                  IMAGE = UTILS_MODULE.getAppConfigItem(".integration.image")
                  REGISTRY = UTILS_MODULE.getAppConfigItem(".integration.registry.url")
                  def files = findFiles glob: UTILS_MODULE.getAppConfigItem(".deployment.composeFilePattern")
                  def fileNames = files.collect { it.name }
                  def userParams = input message: 'Provide deployment details',
                          parameters: [
                                  string(
                                          description: "Allows to override the release version!",
                                          defaultValue: "1.0.0",
                                          name: 'VERSION'
                                  ),
                                  choice(
                                          choices: fileNames,
                                          description: 'Docker compose stack to deploy. Might contain multiple apps.',
                                          name: 'STACK'
                                  )
                          ]
          
                  RELEASE_VERSION = userParams["VERSION"]
                  STACK = userParams["STACK"]
              }
          }
          
          void generateSteps() {
              stage('Deploy') {
                  node('builder-x86') {
                      PARENT_MODULE.setup() //Reinitialize from CD.groovy as we might be jumping across agents
                      withEnv([
                              "VERSION=${BUILD_VERSION}",
                              "IMAGE=${IMAGE}",
                              "REGISTRY=${REGISTRY}"
                      ]) {
                          sh(
                                  script: "docker stack deploy -c ${STACK} ${STACK.replaceAll(".yaml", "")}"
                          )
                          sh(
                                  script: "docker stack services ${STACK.replaceAll(".yaml", "")}"
                          )
                      }
                  }
              }
          }
          
          return this
           
          mulgish Juri Duval added a comment - dnusbaum DOCKER_COMPOSE.groovy contains below code: package core.cd import groovy.transform.Field @Field String IMAGE @Field String REGISTRY @Field String STACK void prepareDeployment() { timeout(5) { IMAGE = UTILS_MODULE.getAppConfigItem( ".integration.image" ) REGISTRY = UTILS_MODULE.getAppConfigItem( ".integration.registry.url" ) def files = findFiles glob: UTILS_MODULE.getAppConfigItem( ".deployment.composeFilePattern" ) def fileNames = files.collect { it.name } def userParams = input message: 'Provide deployment details' , parameters: [ string( description: "Allows to override the release version!" , defaultValue: "1.0.0" , name: 'VERSION' ), choice( choices: fileNames, description: 'Docker compose stack to deploy. Might contain multiple apps.' , name: 'STACK' ) ] RELEASE_VERSION = userParams[ "VERSION" ] STACK = userParams[ "STACK" ] } } void generateSteps() { stage( 'Deploy' ) { node( 'builder-x86' ) { PARENT_MODULE.setup() //Reinitialize from CD.groovy as we might be jumping across agents withEnv([ "VERSION=${BUILD_VERSION}" , "IMAGE=${IMAGE}" , "REGISTRY=${REGISTRY}" ]) { sh( script: "docker stack deploy -c ${STACK} ${STACK.replaceAll(" .yaml ", " ")}" ) sh( script: "docker stack services ${STACK.replaceAll(" .yaml ", " ")}" ) } } } } return this
          dnusbaum Devin Nusbaum added a comment -

          andreyakostovsap Thank you for confirming!

          mulgish Ok great, so as far as I understand, the problem is with the fields like @Field String X in DOCKER_COMPOSE.groovy. You could try changing them to @Field String X = null as a workaround.

          Thank you both for helping!

          dnusbaum Devin Nusbaum added a comment - andreyakostovsap Thank you for confirming! mulgish  Ok great, so as far as I understand, the problem is with the fields like @Field String X in DOCKER_COMPOSE.groovy. You could try changing them to @Field String X = null as a workaround. Thank you both for helping!
          danielbeck Daniel Beck added a comment -

          https://repo.jenkins-ci.org/incrementals/org/jenkins-ci/plugins/script-security/1185.vd0f990194db_8/ is a CI build with the proposed fix. Download the .hpi file and install manually.

          Note that despite the similar looking version number, this is not an official release (and may even result in a future official release not being offered for installation, requiring manual installation).

          danielbeck Daniel Beck added a comment - https://repo.jenkins-ci.org/incrementals/org/jenkins-ci/plugins/script-security/1185.vd0f990194db_8/ is a CI build with the proposed fix. Download the .hpi file and install manually. Note that despite the similar looking version number, this is not an official release (and may even result in a future official release not being offered for installation, requiring manual installation).
          dnusbaum Devin Nusbaum added a comment -

          A fix for this issue was just released in Script Security Plugin version 1189.vb_a_b_7c8fd5fde.

          dnusbaum Devin Nusbaum added a comment - A fix for this issue was just released in Script Security Plugin version 1189.vb_a_b_7c8fd5fde .

          People

            dnusbaum Devin Nusbaum
            andreyakostovsap Andreya Kostov
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: