Copilot commented on code in PR #2779:
URL: https://github.com/apache/groovy/pull/2779#discussion_r3753644298
##########
src/test/groovy/org/codehaus/groovy/control/customizers/SecureASTCustomizerTest.groovy:
##########
@@ -754,4 +754,171 @@ final class SecureASTCustomizerTest {
'''
}
}
+
+
//--------------------------------------------------------------------------
+ // code outside method bodies: constructors and initializers
+
+ private void disallowSystemReceiver() {
+ customizer.disallowedReceivers = ['java.lang.System']
+ }
+
+ @Test
+ void testDisallowedReceiverInScriptBody() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate "System.getProperty('java.version')"
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverInConstructor() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ class A { A() { System.getProperty('java.version') } }
+ new A()
+ '''
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverInStaticInitializer() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ class A { static { System.getProperty('java.version') } }
+ new A()
+ '''
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverInObjectInitializer() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ class A { { System.getProperty('java.version') } }
+ new A()
+ '''
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverInFieldInitializer() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ class A { def f = System.getProperty('java.version') }
+ new A()
+ '''
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverInStaticFieldInitializer() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ class A { static def f = System.getProperty('java.version') }
+ new A()
+ '''
+ }
+ }
+
+ @Test
+ void testGeneratedScriptConstructorsAreNotChecked() {
+ // every script class has generated constructors which call
super(Binding); they are not
+ // written by the author of the script, so they must not be subject to
the restrictions
+ customizer.with {
+ disallowedReceivers = ['java.lang.System']
+ allowedExpressions = [BinaryExpression, ConstantExpression]
+ }
+ def shell = new GroovyShell(configuration)
+ shell.evaluate '1 + 1'
+ // no error means success
+ }
+
+ @Test
+ void testDisallowedReceiverMovedIntoGeneratedConstructor() {
+ // @TupleConstructor(pre=...) relocates the closure body into the
constructor it generates;
+ // the statements keep their original source position, so they are
still the author's code
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ @groovy.transform.TupleConstructor(pre={
System.getProperty('java.version') })
+ class A { String a }
+ new A('x')
+ '''
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverMovedIntoGeneratedMapConstructor() {
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ @groovy.transform.MapConstructor(pre={
System.getProperty('java.version') })
+ class A { String a }
+ null
+ '''
+ }
+ }
+
+ @Test
+ void testDisallowedReceiverMovedIntoSyntheticMethod() {
+ // @ConditionalInterrupt lifts its closure into a synthetic method and
calls it at every
+ // method start and every loop; the closure is still code the script
author wrote
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ assert hasSecurityException {
+ shell.evaluate '''
+ import groovy.transform.ConditionalInterrupt
+ @ConditionalInterrupt({ System.getProperty('java.version') !=
null })
+ class A { def m() { 1 } }
+ null
+ '''
+ }
+ }
+
+ @Test
+ void testGeneratedSyntheticMethodsAreNotChecked() {
+ // the compiler emits a great many synthetic methods -- property
accessors, delegate
+ // forwarders, record components, enum machinery, trait bridges. Their
contents carry no
+ // source position and must stay exempt, otherwise the filter is doing
nothing
+ disallowSystemReceiver()
+ def shell = new GroovyShell(configuration)
+ shell.evaluate '''
+ trait T { def hi() { 1 } }
+ class A implements T { String p; int q }
+ class B { @Delegate List l = [] }
+ record R(String a, int b) {}
+ enum E { X, Y }
+ new A(); new B(); new R('s', 1); E.X
+ '''
+ // no error means success
+ }
Review Comment:
`testGeneratedSyntheticMethodsAreNotChecked` doesn’t currently validate the
behavior described in its comment: it only sets `disallowedReceivers` to
`java.lang.System`, but the evaluated script never references `System`, so the
test will pass even if synthetic methods were inadvertently checked. Consider
making the test fail-fast if any generated synthetic method statements
unexpectedly carry a source position (e.g., by using a very restrictive
`allowedExpressions` set and a script with no authored expressions beyond a
constant).
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]