-
Notifications
You must be signed in to change notification settings - Fork 189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[JENKINS-70108] CastBlock must contextualize invoker to avoid issues in mixed-trust scenarios #640
Changes from all commits
d392eaf
041de1c
a9cf86c
de1d17c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -527,7 +527,7 @@ public void regexpOperator() throws Throwable { | |
@Test | ||
public void arrayPassedToMethod() throws Throwable { | ||
assertEvaluate(4, "def m(x) {x.size()}; def a = [1, 2]; a.size() + m(a)"); // control case | ||
assertEvaluate(4, "def m(x) {x.size()}; def a = [1, 2].toArray(); a.length + m(List.of(a))"); // workaround #1 | ||
assertEvaluate(4, "def m(x) {x.size()}; def a = [1, 2].toArray(); a.length + m(Arrays.asList(a))"); // workaround #1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See #635 (comment). |
||
assertEvaluate(4, "@NonCPS def m(x) {x.length}; def a = [1, 2].toArray(); a.length + m(a)"); // workaround #2 | ||
assertEvaluate(4, "def m(x) {x.length}; def a = [1, 2].toArray(); a.length + m(a)"); // formerly: groovy.lang.MissingPropertyException: No such property: length for class: java.lang.Integer | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -696,4 +696,44 @@ public void sandboxInterceptsImplicitCastsArrayAssignment() throws Throwable { | |
"Script1.method3()"); | ||
} | ||
|
||
@Issue("JENKINS-70108") | ||
@Test public void castsInTrustedCodeCalledByUntrustedCodeShouldNotBeIntercepted() throws Throwable { | ||
TrustedCpsCompiler trusted = new TrustedCpsCompiler(); | ||
trusted.setUp(); | ||
getBinding().setVariable("trusted", trusted.getCsh().parse("def foo() { File f = ['secret.key'] }")); | ||
assertIntercept( | ||
"trusted.foo()", // Untrusted script | ||
new File("secret.key"), | ||
"Script1.super(Script1).setBinding(Binding)", | ||
"Script1.trusted", | ||
"Script1.foo()"); | ||
Comment on lines
+707
to
+709
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, we would also intercept |
||
} | ||
|
||
/* | ||
* All blocks that perform boolean casts internally using ContinuationGroup.castToBoolean incorrectly intercept | ||
* calls performed by the cast when the cast is in trusted code that is called by untrusted code. For boolean | ||
* casts, this is not that interesting, since it only causes problems in practice if you cast a type that | ||
* implements an asBoolean method that would not be allowed by the sandbox. | ||
* I see two obvious ways to fix this: | ||
* 1. Add a CallSiteBlock parameter to ContinuationGroup.castToBoolean, and use it to contextualize the invoker | ||
* used in that method. All Blocks that use the method would need to be updated to implement CallSiteBlock. | ||
Comment on lines
+718
to
+719
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like the simplest option. Let me know if you think it's worth fixing. There are eight |
||
* 2. Delete ContinuationGroup.castToBoolean and replace all uses with basic Java casts to boolean. Modify | ||
* CpsTransformer to insert explicit boolean casts into the CPS-transformed program for all AST nodes whose | ||
* Blocks previously used castToBoolean. | ||
*/ | ||
@Ignore("This variant of JENKINS-70108 seems less likely to cause problems in practice and is more complex to fix") | ||
@Test public void booleanCastsInTrustedCodeCalledByUntrustedCodeShouldNotBeIntercepted() throws Throwable { | ||
TrustedCpsCompiler trusted = new TrustedCpsCompiler(); | ||
trusted.setUp(); | ||
getBinding().setVariable("trusted", trusted.getCsh().parse( | ||
"class Test { @NonCPS def asBoolean() { false } }\n" + | ||
"def foo() { if (new Test()) { 123 } else { 456 } }")); | ||
assertIntercept( | ||
"trusted.foo()", // Untrusted script | ||
456, | ||
"Script1.super(Script1).setBinding(Binding)", | ||
"Script1.trusted", | ||
"Script1.foo()"); | ||
// Currently the call to Test.asBoolean() is also intercepted, which is incorrect. | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I verified that this does not cause problems deserializing
CastBlock
s serialized before this change.