Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

Five ways a Groovy sandbox leaked, and what that says about sandbox design

Five ways a Groovy sandbox leaked, and what that says about sandbox design Jenkins published five Script Security Plugin advisories on 16 September 2026. Each describes a separate route from a sandboxed Pipeline script

Five ways a Groovy sandbox leaked, and what that says about sandbox design

Jenkins published five Script Security Plugin advisories on 16 September 2026. Each describes a separate route from a sandboxed Pipeline script to arbitrary code execution in the Jenkins controller JVM. Each one is rated 8.8 or 7.5. Read individually they are five bugs. Read together they are a specification of where sandbox boundaries fail.

The five defects

CVE-2026-92122 concerns the proxy created when a sandboxed script coerces a value to an interface. The plugin does not check the method invoked through that proxy if the value inherits a method with the same name as an interface method.
CVE-2026-92123 concerns a null receiver. Method calls, property accesses, attribute accesses and array accesses performed on null are not intercepted, so the operations escape the check that would otherwise apply.
CVE-2026-92124 concerns a cast. When a script casts a collection to another type, the plugin checks the operations Groovy will perform on the collection itself rather than on the elements later read from it.
CVE-2026-92125 concerns an annotation. The plugin does not reject @GroovyASTTransformationClass, which lets an attacker register an AST transformation that runs at compile time, before any runtime sandbox check is in a position to inspect it.
CVE-2026-92129 concerns dynamic methods added to a class at runtime. Calls to those methods from sandboxed scripts are not checked.
The affected version string in every advisory is 1415.v9a_f9b_3a_c253d and earlier.

The pattern behind the list

Four of the five are interception gaps, and each sits at a place where the language semantics diverge from the sandbox author's mental model.
A Groovy sandbox typically works by subclassing the language's dispatch components and checking each call before it executes. That design assumes every operation reaches one of those components. Proxy objects, null receivers, collection element access, and runtime-added methods are all places where the operation arrives through a different door.
The fifth is different in kind. An AST transformation runs during compilation, so a sandbox that inspects runtime calls is structurally unable to see it. Rejecting the annotation is the only available control at that layer.
The gaps cluster around language features that a reviewer would not think to test, because the sandbox documentation does not describe them as features in scope.

What an attacker needs to reach them

Every advisory carries the same precondition: permission to define and run sandboxed scripts, including Pipelines. Jenkins assigns that permission through job creation rights, and the population that holds it in a typical installation is larger than the population that holds administrative rights.
The CVSS vectors rate the attack as PR:L, network-accessible, no user interaction. On an unpatched controller, a user who can commit a Jenkinsfile to a multibranch pipeline, or who can edit a Pipeline job's script, meets the precondition.
That is the sentence worth carrying into a risk conversation. The sandbox exists because that population is not assumed to be fully trusted with controller access, and these five advisories are five separate demonstrations that the assumption was not met.

Remediation and the upgrade question

The fix is to upgrade the Script Security Plugin past 1415.v9a_f9b_3a_c253d. Because all five issues share the same affected version string and the same fix path, one upgrade closes the set.
The operational problem is not the upgrade itself. It is that the Script Security Plugin is often pinned for compatibility with other plugins, and pinning is exactly how an installation stays on a vulnerable version after the advisory is published. Jenkins' plugin manager shows the installed version, and the check worth automating is a comparison of that string against the advisory list.
Reducing job-definition rights is a compensating control, not a fix. It narrows the population that can reach an unpatched gap, and it does nothing about the gap.

Testing the sandbox rather than trusting it

The five issues suggest a specific test approach for anyone running Jenkins. Instead of testing whether the sandbox blocks a known dangerous call, test the language features around which the interception layer is built.
Coercion to an interface, operations on null, casts of collections, annotation-driven transformations, and metaprogramming that adds methods at runtime are the five categories these CVEs name. A regression suite that exercises each one against the plugin version in use would have detected the same defects and will detect their successors.
That suite does not need to be an exploit. It needs to assert the expected behaviour: an operation that should be rejected is rejected. Where the assertion fails, the sandbox has a gap regardless of whether a CVE has been assigned.

The record after abuse

A sandbox escape runs arbitrary code on the controller, so the record of the escape is whatever the attacker chose to leave. The compensating controls are therefore architectural.
Controller-level audit plugins log job creation and configuration changes with the authenticated user, which identifies who could have supplied the script. Build logs show what the pipeline printed, which is unlikely to include the escape itself. Agent console logs are ephemeral by default.
The practical comparison is whether a specific Pipeline definition can be reconstructed from an audit trail a month after the fact. Where the answer depends on a git history for a Jenkinsfile that was never committed, the record has a gap.

References

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.