jenkinsci / jenkinsci/workflow-cps-plugin
[JENKINS-69965] Groovy security check confused by custom coercion Src.asType(Class c)
- 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)
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
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