jenkinsci / jenkinsci/workflow-cps-plugin

[JENKINS-69965] Groovy security check confused by custom coercion Src.asType(Class c)

Open
#1,645 0 comments 0 reactions 0 assignees View on GitHub
component:workflow-cps-plugin imported-jira-issue priority:minor resolution:unresolved
Dominant language
Java
Stars
186
Forks
213
Avg merge
12h 12m
Merged PRs (30d)
8

Description

Here is a basic case:

interface B {

void foo();
}
class A {
Object asType(Class cls) {
assert 0; // not called
}
}
A a = new A();
Object o = a as B;

In groovy, this would assert because "a as B" would call a.asType(B).

In sandbox, this ends up raising a security exception "Scripts not permitted to use method GroovyObject invokeMethod String Object (A foo)".

The exception is raised from org.kohsuke.groovy.sandbox.impl.Checker.preCheckedCast.  I believe the second block clazz.isInterface() is iterating over the methods of B (clazz), checking that they can be called.  But, it is passing a (exp) to the method call.  Then, that seems to throw an exception because there is no a.foo method, and it would resort to a.invokeMethod(...), which is not allowed.

It seems to be thinking that a (exp) implements clazz.  However, here, (a as B) should call a.asType(B).  Also, it would seem the previous clazz.isAssignableFrom(exp.getClass()) would be true if a implements B.  So, it isn't clear (to me) what that check is doing.

Workaround in this case, where I know that there is a custom asType, is to use a.asType(B), and that works correctly.

---
Originally reported by vallon, imported from: Groovy security check confused by custom coercion Src.asType(Class c)


  • status: Open
  • priority: Minor
  • component(s): workflow-cps-plugin
  • label(s): cps, workflow-cps-plugin
  • resolution: Unresolved
  • votes: 0
  • watchers: 1
  • imported: 2025-12-07

Raw content of original issue

Here is a basic case:



interface B {

void foo();
}
class A {
Object asType(Class cls) {
assert 0; // not called
}
}
A a = new A();
Object o = a as B;


In groovy, this would assert because "a as B" would call a.asType(B).

In sandbox, this ends up raising a security exception "Scripts not permitted to use method GroovyObject invokeMethod String Object (A foo)".

The exception is raised from org.kohsuke.groovy.sandbox.impl.Checker.preCheckedCast.  I believe the second block clazz.isInterface() is iterating over the methods of B (clazz), checking that they can be called.  But, it is passing a (exp) to the method call.  Then, that seems to throw an exception because there is no a.foo method, and it would resort to a.invokeMethod(...), which is not allowed.

It seems to be thinking that a (exp) implements clazz.  However, here, (a as B) should call a.asType(B).  Also, it would seem the previous clazz.isAssignableFrom(exp.getClass()) would be true if a implements B.  So, it isn't clear (to me) what that check is doing.

Workaround in this case, where I know that there is a custom asType, is to use a.asType(B), and that works correctly.

  • environment: Jenkins 2.332.3, workflow-cps-plugin 2692.v76b_089ccd026, unknown OS, Java.

Contributor guide

Open the contributing guide

Research direction

Start at org.kohsuke.groovy.sandbox.impl.Checker.preCheckedCast and reproduce the issue with the A, B, and custom asType example from the report. Trace why the interface check attempts B.foo on A instead of honoring A.asType(B). Done means the sandbox no longer raises the unexpected invokeMethod security exception for this coercion.

Written by the indexing model from the issue text.

Assessment

Tech stack
groovy, java
Domain
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.