[groovy] 01/02: backport GROOVY-8163 sandbox fix

Felix Natter fnatter-guest at moszumanska.debian.org
Thu Sep 7 18:11:48 UTC 2017


This is an automated email from the git hooks/post-receive script.

fnatter-guest pushed a commit to branch master
in repository groovy.

commit ced0ef139e0487f011650ca7894ba7936e659ddd
Author: Felix Natter <fnatter at gmx.net>
Date:   Thu Sep 7 20:09:42 2017 +0200

    backport GROOVY-8163 sandbox fix
---
 debian/patches/08_GROOVY-8163.patch | 477 ++++++++++++++++++++++++++++++++++++
 debian/patches/series               |   1 +
 2 files changed, 478 insertions(+)

diff --git a/debian/patches/08_GROOVY-8163.patch b/debian/patches/08_GROOVY-8163.patch
new file mode 100644
index 0000000..088ec5f
--- /dev/null
+++ b/debian/patches/08_GROOVY-8163.patch
@@ -0,0 +1,477 @@
+From 0305a38a0cc8f4190a1486c460ebc6f712ad1a07 Mon Sep 17 00:00:00 2001
+From: Dimitry Polivaev <dpolivaev at gmx.de>
+Date: Sat, 3 Jun 2017 20:03:18 +0200
+Subject: [PATCH] GROOVY-8163: Prevent CachedField and CachedMethod from
+ leaking access permissions (closes #532)
+
+---
+ src/main/groovy/lang/MetaClassImpl.java            |   3 +
+ .../groovy/reflection/AccessPermissionChecker.java |  83 +++++++
+ .../reflection/CacheAccessControlException.java    |  27 ++
+ .../codehaus/groovy/reflection/CachedField.java    |   2 +
+ .../codehaus/groovy/reflection/CachedMethod.java   |   8 +
+ .../codehaus/groovy/reflection/SecurityTest.java   | 271 +++++++++++++++++++++
+ 6 files changed, 394 insertions(+)
+ create mode 100644 src/main/org/codehaus/groovy/reflection/AccessPermissionChecker.java
+ create mode 100644 src/main/org/codehaus/groovy/reflection/CacheAccessControlException.java
+ create mode 100644 src/test/org/codehaus/groovy/reflection/SecurityTest.java
+
+--- a/src/main/groovy/lang/MetaClassImpl.java
++++ b/src/main/groovy/lang/MetaClassImpl.java
+@@ -23,6 +23,7 @@
+ import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+ import org.codehaus.groovy.control.CompilationUnit;
+ import org.codehaus.groovy.control.Phases;
++import org.codehaus.groovy.reflection.CacheAccessControlException;
+ import org.codehaus.groovy.reflection.CachedClass;
+ import org.codehaus.groovy.reflection.CachedConstructor;
+ import org.codehaus.groovy.reflection.CachedField;
+@@ -1809,6 +1810,9 @@
+             } catch (IllegalArgumentException e) {
+                 // can't access the field directly but there may be a getter
+                 mp = null;
++            } catch (CacheAccessControlException e) {
++                // can't access the field directly but there may be a getter
++                mp = null;
+             }
+         }
+ 
+--- /dev/null
++++ b/src/main/org/codehaus/groovy/reflection/AccessPermissionChecker.java
+@@ -0,0 +1,83 @@
++/*
++ *  Licensed to the Apache Software Foundation (ASF) under one
++ *  or more contributor license agreements.  See the NOTICE file
++ *  distributed with this work for additional information
++ *  regarding copyright ownership.  The ASF licenses this file
++ *  to you under the Apache License, Version 2.0 (the
++ *  "License"); you may not use this file except in compliance
++ *  with the License.  You may obtain a copy of the License at
++ *
++ *    http://www.apache.org/licenses/LICENSE-2.0
++ *
++ *  Unless required by applicable law or agreed to in writing,
++ *  software distributed under the License is distributed on an
++ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ *  KIND, either express or implied.  See the License for the
++ *  specific language governing permissions and limitations
++ *  under the License.
++ */
++package org.codehaus.groovy.reflection;
++
++import java.lang.reflect.Field;
++import java.lang.reflect.Method;
++import java.lang.reflect.Modifier;
++import java.lang.reflect.ReflectPermission;
++import java.security.AccessControlException;
++
++import groovy.lang.GroovyObject;
++
++class AccessPermissionChecker {
++
++    private static final ReflectPermission REFLECT_PERMISSION = new ReflectPermission("suppressAccessChecks");
++
++    private static void checkAccessPermission(Class<?> declaringClass, final int modifiers, boolean isAccessible) {
++        final SecurityManager securityManager = System.getSecurityManager();
++        if (securityManager != null && isAccessible) {
++                if (((modifiers & Modifier.PRIVATE) != 0
++                        || ((modifiers & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0
++                             && packageCanNotBeAddedAnotherClass(declaringClass)))
++                        && !GroovyObject.class.isAssignableFrom(declaringClass)) {
++                        securityManager.checkPermission(REFLECT_PERMISSION);
++                }
++                else if ((modifiers & (Modifier.PROTECTED)) != 0
++                    && declaringClass.equals(ClassLoader.class)){
++                    securityManager.checkCreateClassLoader();
++                }
++        }
++    }
++
++    private static boolean packageCanNotBeAddedAnotherClass(Class<?> declaringClass) {
++        return declaringClass.getName().startsWith("java.");
++    }
++
++    static void checkAccessPermission(Method method) {
++        try {
++            checkAccessPermission(method.getDeclaringClass(), method.getModifiers(), method.isAccessible());
++        } catch (AccessControlException e) {
++            throw createCacheAccessControlExceptionOf(method, e);
++        }
++    }
++
++    private static CacheAccessControlException createCacheAccessControlExceptionOf(Method method, AccessControlException e) {
++        return new CacheAccessControlException(
++                "Groovy object can not access method " + method.getName()  + " cacheAccessControlExceptionOf class " + method.getDeclaringClass().getName()
++                        + " with modifiers \"" + Modifier.toString(method.getModifiers()) + "\"", e);
++    }
++
++    static void checkAccessPermission(Field field) {
++        try {
++            checkAccessPermission(field.getDeclaringClass(), field.getModifiers(), field.isAccessible());
++        } catch (AccessControlException e) {
++            throw createCacheAccessControlExceptionOf(field, e);
++        }
++    }
++
++    private static CacheAccessControlException createCacheAccessControlExceptionOf(Field field, AccessControlException e) {
++        return new CacheAccessControlException(
++                "Groovy object can not access field " + field.getName()  + " cacheAccessControlExceptionOf class " + field.getDeclaringClass().getName()
++                        + " with modifiers \"" + Modifier.toString(field.getModifiers()) + "\"", e);
++    }
++
++    private AccessPermissionChecker() {}
++
++}
+--- /dev/null
++++ b/src/main/org/codehaus/groovy/reflection/CacheAccessControlException.java
+@@ -0,0 +1,27 @@
++/*
++ *  Licensed to the Apache Software Foundation (ASF) under one
++ *  or more contributor license agreements.  See the NOTICE file
++ *  distributed with this work for additional information
++ *  regarding copyright ownership.  The ASF licenses this file
++ *  to you under the Apache License, Version 2.0 (the
++ *  "License"); you may not use this file except in compliance
++ *  with the License.  You may obtain a copy of the License at
++ *
++ *    http://www.apache.org/licenses/LICENSE-2.0
++ *
++ *  Unless required by applicable law or agreed to in writing,
++ *  software distributed under the License is distributed on an
++ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ *  KIND, either express or implied.  See the License for the
++ *  specific language governing permissions and limitations
++ *  under the License.
++ */
++package org.codehaus.groovy.reflection;
++
++import groovy.lang.GroovyRuntimeException;
++
++public class CacheAccessControlException extends GroovyRuntimeException{
++    public CacheAccessControlException(String message, Throwable cause) {
++        super(message, cause);
++    }
++}
+--- a/src/main/org/codehaus/groovy/reflection/CachedField.java
++++ b/src/main/org/codehaus/groovy/reflection/CachedField.java
+@@ -50,6 +50,7 @@
+      * @throws RuntimeException if the property could not be evaluated
+      */
+     public Object getProperty(final Object object) {
++        AccessPermissionChecker.checkAccessPermission(field);
+         try {
+             return field.get(object);
+         } catch (IllegalAccessException e) {
+@@ -65,6 +66,7 @@
+      * @throws RuntimeException if the property could not be set
+      */
+     public void setProperty(final Object object, Object newValue) {
++        AccessPermissionChecker.checkAccessPermission(field);
+         final Object goalValue = DefaultTypeTransformation.castToType(newValue, field.getType());
+ 
+         if (isFinal()) {
+--- a/src/main/org/codehaus/groovy/reflection/CachedMethod.java
++++ b/src/main/org/codehaus/groovy/reflection/CachedMethod.java
+@@ -90,6 +90,12 @@
+ 
+     public final Object invoke(Object object, Object[] arguments) {
+         try {
++            AccessPermissionChecker.checkAccessPermission(cachedMethod);
++        }
++        catch (CacheAccessControlException ex) {
++            throw new InvokerInvocationException(ex);
++        }
++        try {
+             return cachedMethod.invoke(object, arguments);
+         } catch (IllegalArgumentException e) {
+             throw new InvokerInvocationException(e);
+@@ -124,6 +130,7 @@
+     }
+ 
+     public final Method setAccessible() {
++        AccessPermissionChecker.checkAccessPermission(cachedMethod);
+ //        if (queuedToCompile.compareAndSet(false,true)) {
+ //            if (isCompilable())
+ //              CompileThread.addMethod(this);
+@@ -324,6 +331,7 @@
+     }
+ 
+     public Method getCachedMethod() {
++        AccessPermissionChecker.checkAccessPermission(cachedMethod);
+         return cachedMethod;
+     }
+ 
+--- /dev/null
++++ b/src/test/org/codehaus/groovy/reflection/SecurityTest.java
+@@ -0,0 +1,271 @@
++/*
++ *  Licensed to the Apache Software Foundation (ASF) under one
++ *  or more contributor license agreements.  See the NOTICE file
++ *  distributed with this work for additional information
++ *  regarding copyright ownership.  The ASF licenses this file
++ *  to you under the Apache License, Version 2.0 (the
++ *  "License"); you may not use this file except in compliance
++ *  with the License.  You may obtain a copy of the License at
++ *
++ *    http://www.apache.org/licenses/LICENSE-2.0
++ *
++ *  Unless required by applicable law or agreed to in writing,
++ *  software distributed under the License is distributed on an
++ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ *  KIND, either express or implied.  See the License for the
++ *  specific language governing permissions and limitations
++ *  under the License.
++ */
++package org.codehaus.groovy.reflection;
++
++import groovy.lang.GroovyObjectSupport;
++import groovy.util.GroovyTestCase;
++import org.codehaus.groovy.runtime.InvokerInvocationException;
++import java.lang.reflect.Field;
++import java.lang.reflect.Method;
++import java.lang.reflect.ReflectPermission;
++import java.nio.ByteBuffer;
++import java.security.AccessControlException;
++import java.security.Permission;
++import java.security.Permissions;
++import java.security.ProtectionDomain;
++
++public class SecurityTest extends GroovyTestCase {
++
++    @SuppressWarnings("unused")
++    public class TestClass{
++        public String publicField;
++        protected String protectedField;
++        String packagePrivateField;
++        private String privateField;
++
++        private boolean methodCalled = false;
++
++        public void publicMethod() {
++            privateMethod();
++        }
++
++        private void privateMethod() {
++            methodCalled = true;
++        }
++
++        void packagePrivateMethod() {
++            privateMethod();
++        }
++
++        void protectedMethod() {
++            privateMethod();
++        }
++
++        public boolean isMethodCalled() {
++            return methodCalled;
++        }
++    }
++
++    @SuppressWarnings("unused")
++    public class TestGroovyClass extends GroovyObjectSupport{
++        private String privateField;
++        private boolean methodCalled = false;
++        private void privateMethod() {
++            methodCalled = true;
++        }
++        public boolean isMethodCalled() {
++            return methodCalled;
++        }
++    }
++    SecurityManager restrictiveSecurityManager;
++    CachedMethod cachedMethodUnderTest;
++    CachedField cachedFieldUnderTest;
++    Permissions forbidden;
++
++    public void setUp() {
++        forbidden = new Permissions();
++        forbidden.add(new ReflectPermission("suppressAccessChecks"));
++        restrictiveSecurityManager = new SecurityManager() {
++
++            @Override
++            public void checkPermission(Permission perm) {
++                if (forbidden.implies(perm))
++                    throw new AccessControlException(perm.getName());
++            }
++        };
++    }
++
++    public void tearDown(){
++        System.setSecurityManager(null);
++    }
++
++    private CachedMethod createCachedMethod(String name) throws Exception {
++        return createCachedMethod(TestClass.class, name);
++    }
++
++    private CachedMethod createCachedMethod(Class<?> cachedClass, String methodName, Class... parameters) throws NoSuchMethodException {
++        Method method = cachedClass.getDeclaredMethod(methodName, parameters);
++        method.setAccessible(true);
++        return new CachedMethod(null, method);
++    }
++
++    private boolean invokesCachedMethod() {
++        TestClass object = new TestClass();
++        cachedMethodUnderTest.invoke(object, new Object[]{});
++        return object.isMethodCalled();
++    }
++
++    private CachedField createCachedField(String name) throws Exception {
++        Field field = TestClass.class.getDeclaredField(name);
++        field.setAccessible(true);
++        return new CachedField(field);
++    }
++
++    public void testInvokesPublicMethodsWithoutChecks() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("publicMethod");
++        System.setSecurityManager(restrictiveSecurityManager);
++        assertTrue(invokesCachedMethod());
++    }
++
++    public void testReturnsAccesiblePublicMethodsWithoutChecks() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("publicMethod");
++        System.setSecurityManager(restrictiveSecurityManager);
++        assertEquals("publicMethod", cachedMethodUnderTest.setAccessible().getName());
++        assertEquals("publicMethod", cachedMethodUnderTest.getCachedMethod().getName());
++    }
++
++    public void testAccessesPublicFieldsWithoutChecks() throws Exception {
++        cachedFieldUnderTest = createCachedField("publicField");
++        System.setSecurityManager(restrictiveSecurityManager);
++        TestClass object = new TestClass();
++        cachedFieldUnderTest.setProperty(object, "value");
++        assertEquals("value", cachedFieldUnderTest.getProperty(object));
++    }
++
++    public void testInvokesPrivateMethodsWithoutSecurityManager() throws Exception{
++        cachedMethodUnderTest = createCachedMethod("privateMethod");
++        assertTrue(invokesCachedMethod());
++    }
++
++    public void testAccessesPrivateFieldsWithoutSecurityManager() throws Exception {
++        cachedFieldUnderTest = createCachedField("privateField");
++        System.setSecurityManager(null);
++        TestClass object = new TestClass();
++        cachedFieldUnderTest.setProperty(object, "value");
++        assertEquals("value", cachedFieldUnderTest.getProperty(object));
++    }
++
++    public void testReturnsAccesiblePrivateMethodsWithoutSecurityManager() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("privateMethod");
++        System.setSecurityManager(null);
++        assertEquals("privateMethod", cachedMethodUnderTest.setAccessible().getName());
++        assertEquals("privateMethod", cachedMethodUnderTest.getCachedMethod().getName());
++    }
++
++    public void testChecksReflectPermissionForInvokeOnPrivateMethods() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("privateMethod");
++        System.setSecurityManager(restrictiveSecurityManager);
++        try {
++            invokesCachedMethod();
++            fail();
++        }
++        catch (InvokerInvocationException e) {
++            assertEquals(CacheAccessControlException.class, e.getCause().getClass());
++        }
++    }
++
++    public void testChecksReflectPermissionForFieldAccessOnPrivateFields() throws Exception {
++        cachedFieldUnderTest = createCachedField("privateField");
++        System.setSecurityManager(restrictiveSecurityManager);
++        TestClass object = new TestClass();
++        try {
++            cachedFieldUnderTest.setProperty(object, "value");
++            fail();
++        }
++        catch (CacheAccessControlException e) {
++        }
++
++        try {
++            cachedFieldUnderTest.getProperty(object);
++            fail();
++        }
++        catch (CacheAccessControlException e) {
++        }
++    }
++
++    public void testChecksReflectPermissionForMethodAccessOnPrivateMethods() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("privateMethod");
++        System.setSecurityManager(restrictiveSecurityManager);
++        try {
++            cachedMethodUnderTest.setAccessible();
++            fail();
++        }
++        catch (CacheAccessControlException e) {
++        }
++
++        try {
++            cachedMethodUnderTest.getCachedMethod();
++            fail();
++        }
++        catch (CacheAccessControlException e) {
++        }
++    }
++
++    public void testInvokesPackagePrivateMethodsWithoutChecksInNonRestrictedPackages() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("packagePrivateMethod");
++        System.setSecurityManager(restrictiveSecurityManager);
++        assertTrue(invokesCachedMethod());
++    }
++
++    public void testChecksReflectPermissionForInvokeOnPackagePrivateMethodsInRestrictedJavaPackages() throws Exception {
++        cachedMethodUnderTest = createCachedMethod(ClassLoader.class, "getBootstrapClassPath", new Class[0]);
++        System.setSecurityManager(restrictiveSecurityManager);
++
++        try {
++            cachedMethodUnderTest.invoke(null, new Object[]{});
++            fail();
++        }
++        catch (InvokerInvocationException e) {
++            assertEquals(CacheAccessControlException.class, e.getCause().getClass());
++        }
++    }
++
++    public void testInvokesProtectedMethodsWithoutChecks() throws Exception {
++        cachedMethodUnderTest = createCachedMethod("protectedMethod");
++        System.setSecurityManager(restrictiveSecurityManager);
++        assertTrue(invokesCachedMethod());
++    }
++
++
++    public void testChecksCreateClassLoaderPermissionForClassLoaderProtectedMethodAccess() throws Exception {
++        cachedMethodUnderTest = createCachedMethod(ClassLoader.class, "defineClass", new Class[]{String.class, ByteBuffer.class, ProtectionDomain.class});
++        forbidden = new Permissions();
++        forbidden.add(new RuntimePermission("createClassLoader"));
++        System.setSecurityManager(restrictiveSecurityManager);
++
++        ClassLoader classLoader = getClass().getClassLoader();
++
++        try {
++            cachedMethodUnderTest.invoke(classLoader, new Object[]{null, null, null});
++            fail();
++        }
++        catch (InvokerInvocationException e) {
++            assertEquals(CacheAccessControlException.class, e.getCause().getClass());
++        }
++    }
++
++    public void testInvokesPrivateMethodsInGroovyObjectsWithoutChecks() throws Exception {
++        cachedMethodUnderTest = createCachedMethod(TestGroovyClass.class, "privateMethod");
++        TestGroovyClass object = new TestGroovyClass();
++        System.setSecurityManager(restrictiveSecurityManager);
++        cachedMethodUnderTest.invoke(object, new Object[]{});
++        assertTrue(object.isMethodCalled());
++    }
++
++    public void testAccessesPrivateFieldsInGroovyObjectsWithoutChecks() throws Exception {
++        Field field = TestGroovyClass.class.getDeclaredField("privateField");
++        field.setAccessible(true);
++        cachedFieldUnderTest = new CachedField(field);
++        TestGroovyClass object = new TestGroovyClass();
++        System.setSecurityManager(restrictiveSecurityManager);
++        cachedFieldUnderTest.setProperty(object, "value");
++        assertEquals("value", cachedFieldUnderTest.getProperty(object));
++    }
++
++}
diff --git a/debian/patches/series b/debian/patches/series
index 2ac04eb..e3c14f9 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -6,3 +6,4 @@
 06_ignore_documentation_publication.diff
 07_servlet_api_compatibility.patch
 transition_Gradle_3.1.patch
+08_GROOVY-8163.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/groovy.git



More information about the pkg-java-commits mailing list