The Groovy Sandbox

The Groovy Sandbox and Non-CPS-Transformed Code

Jenkinsfiles come from repositories, so anyone who can push a branch can run Groovy on the controller. The Groovy sandbox (the Script Security plugin) therefore intercepts every method call, constructor and field access and checks it against an allow-list. Anything else is refused:

cps-sandbox: reading a file on the controller from a sandboxed PipelineShell
echo new File('/etc/hostname').text
Output
Scripts not permitted to use new java.io.File java.lang.String. Administrators can decide
  whether ...
...
Finished: FAILURE

The refused signature then waits under Manage Jenkins 8,793 , In-process Script Approval, beside Approve and Deny buttons and a red note that approving it "may introduce a security vulnerability! You are advised to deny it." Approve signatures sparingly: each applies to every Pipeline on the controller. A script run outside the sandbox needs its whole text approved; one created through the REST API failed with UnapprovedUsageException until then.

The second rule concerns @NonCPS. A method with this annotation is compiled as ordinary Groovy: fast, able to use any Java object, but it must not call steps or CPS-transformed code, and it cannot be paused. Mixing the two worlds is the classic Pipeline bug, because it fails silently:

cps-mismatch: closures handed to Java methods, and a @NonCPS method calling CPS codePython
def titles = ['The Quiet Harbor', 'Salt and Saffron', 'Gardens in Glass']
echo "sort with a closure: ${titles.sort { a, b -> a <=> b }}"
def shout(String s) { s.toUpperCase() }
@NonCPS
def shoutAll(List l) { l.collect { shout(it) } }
echo "NonCPS calling CPS: ${shoutAll(titles)}"
Output
expected to call java.util.ArrayList.sort but wound up catching
  org.jenkinsci.plugins.workflow...
sort with a closure: -1
expected to call WorkflowScript.shoutAll but wound up catching WorkflowScript.shout; see: ...
NonCPS calling CPS: THE QUIET HARBOR
Finished: SUCCESS

The build is green, yet the sort returned -1 instead of a list and shoutAll returned one title instead of three. The CPS closure cannot run inside Java's sort, and the @NonCPS method cannot call the CPS method shout. Move such logic entirely into @NonCPS helpers that call nothing CPS-transformed: the same sort inside a @NonCPS method alphabetical(List titles) { titles.toSorted { a, b -> a <=> b } } printed [Gardens in Glass, Salt and Saffron, The Quiet Harbor].

Treat every "expected to call ... but wound up catching" line in a log as a bug (the full line links to jenkins.io/redirect/pipeline-cps-method-mismatches).