jenkinsci / jenkinsci/workflow-cps-plugin

[JENKINS-71524] Using a method reference in a parallel step causes a script security exception

Open
#1,665 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

component:script-security-plugin component:workflow-cps-plugin imported-jira-issue priority:major resolution:unresolved
Dominant language
Java
Stars
186
Forks
212
Avg merge
12h 12m
Merged PRs (30d)
8

Description

When using a method reference as the value in a parallel step's map, I get a SecurityException.
 
Repro case:

A pipeline job with sandbox enabled and this script

def a() {
    println 'hello world'
}

(this.&a)() // this is fine
parallel(['a': { -> this.a() }]) // this is also fine
parallel(['a': this.&a]) // throws SecurityException

Will throw an exception with this stack

java.lang.SecurityException: Rejecting unsandboxed method call: WorkflowScript.a()
    at org.kohsuke.groovy.sandbox.impl.RejectEverythingInterceptor.onMethodCall(RejectEverythingInterceptor.java:44)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:178)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:182)
    at org.kohsuke.groovy.sandbox.impl.SandboxedMethodClosure.doCall(SandboxedMethodClosure.java:26)
    at org.kohsuke.groovy.sandbox.impl.SandboxedMethodClosure.doCall(SandboxedMethodClosure.java:34)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1225)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
    at groovy.lang.Closure.call(Closure.java:420)
    at groovy.lang.Closure.call(Closure.java:414)
    at org.jenkinsci.plugins.workflow.cps.CpsBodyExecution.launch(CpsBodyExecution.java:134)
    at org.jenkinsci.plugins.workflow.cps.CpsBodyInvoker.launch(CpsBodyInvoker.java:188)
    at org.jenkinsci.plugins.workflow.cps.DSL$ThreadTaskImpl.lambda$invokeBodiesAndSwitchToAsyncMode$0(DSL.java:784)
    at org.jenkinsci.plugins.workflow.cps.CpsStepContext.withBodyInvokers(CpsStepContext.java:534)
    at org.jenkinsci.plugins.workflow.cps.DSL$ThreadTaskImpl.invokeBodiesAndSwitchToAsyncMode(DSL.java:774)
    at org.jenkinsci.plugins.workflow.cps.DSL$ThreadTaskImpl.eval(DSL.java:739)
    at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:198)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:420)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:330)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:294)
    at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139)
    at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:30)
    at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:70)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)

This same job will work correctly without the sandbox.

 
This works correctly, even with the sandbox, on our previous Jenkins:

Jenkins 2.121.2
workflow-cps-plugin 2.61
script-security-plugin 1.49

(yes it's been a hot minute since the last upgrade)


Originally reported by sean_sutherland, imported from: Using a method reference in a parallel step causes a script security exception
  • status: Open
  • priority: Major
  • component(s): script-security-plugin, workflow-cps-plugin
  • resolution: Unresolved
  • votes: 1
  • watchers: 2
  • imported: 2025-12-07
Raw content of original issue

When using a method reference as the value in a parallel step's map, I get a SecurityException.   Repro case:

A pipeline job with sandbox enabled and this script

def a() {
    println 'hello world'
}

(this.&a)() // this is fine
parallel(['a': { -> this.a() }]) // this is also fine
parallel(['a': this.&a]) // throws SecurityException

Will throw an exception with this stack

java.lang.SecurityException: Rejecting unsandboxed method call: WorkflowScript.a()
    at org.kohsuke.groovy.sandbox.impl.RejectEverythingInterceptor.onMethodCall(RejectEverythingInterceptor.java:44)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:178)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:182)
    at org.kohsuke.groovy.sandbox.impl.SandboxedMethodClosure.doCall(SandboxedMethodClosure.java:26)
    at org.kohsuke.groovy.sandbox.impl.SandboxedMethodClosure.doCall(SandboxedMethodClosure.java:34)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:98)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1225)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1034)
    at groovy.lang.Closure.call(Closure.java:420)
    at groovy.lang.Closure.call(Closure.java:414)
    at org.jenkinsci.plugins.workflow.cps.CpsBodyExecution.launch(CpsBodyExecution.java:134)
    at org.jenkinsci.plugins.workflow.cps.CpsBodyInvoker.launch(CpsBodyInvoker.java:188)
    at org.jenkinsci.plugins.workflow.cps.DSL$ThreadTaskImpl.lambda$invokeBodiesAndSwitchToAsyncMode$0(DSL.java:784)
    at org.jenkinsci.plugins.workflow.cps.CpsStepContext.withBodyInvokers(CpsStepContext.java:534)
    at org.jenkinsci.plugins.workflow.cps.DSL$ThreadTaskImpl.invokeBodiesAndSwitchToAsyncMode(DSL.java:774)
    at org.jenkinsci.plugins.workflow.cps.DSL$ThreadTaskImpl.eval(DSL.java:739)
    at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:198)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:420)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:330)
    at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:294)
    at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139)
    at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:30)
    at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:70)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:829)

This same job will work correctly without the sandbox.

  This works correctly, even with the sandbox, on our previous Jenkins:

Jenkins 2.121.2
workflow-cps-plugin 2.61
script-security-plugin 1.49

(yes it's been a hot minute since the last upgrade)

environment
CloudBees CI Managed Controller 2.375.3.4-rolling<br/>
workflow-cps-plugin 3606.v0b_d8b_e512dcf<br/>
script-security-plugin 1229.v4880b_b_e905a_6<br/>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the parallel step and the SandboxedMethodClosure and RejectEverythingInterceptor frames in the reported stack trace, comparing method-reference execution with the working closure and direct-call cases. Reproduce the sandboxed pipeline using the versions listed in the environment, then verify that the method reference no longer raises a SecurityException while sandbox protection remains intact.

Written by the indexing model from the issue text.

Assessment

Tech stack
groovy, java
Domain
devtools, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.