| /* |
| * 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.apache.cassandra.cql3.validation.entities; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.junit.Test; |
| |
| import org.apache.cassandra.cql3.CQLTester; |
| import org.apache.cassandra.cql3.functions.UDFByteCodeVerifier; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.CallClone; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.CallComDatastax; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.CallFinalize; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.CallOrgApache; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithField; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithInitializer; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithInitializer2; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithInitializer3; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithInnerClass; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithInnerClass2; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithStaticInitializer; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.ClassWithStaticInnerClass; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.GoodClass; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UseOfSynchronized; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UseOfSynchronizedWithNotify; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UseOfSynchronizedWithNotifyAll; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UseOfSynchronizedWithWait; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UseOfSynchronizedWithWaitL; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UseOfSynchronizedWithWaitLI; |
| import org.apache.cassandra.cql3.validation.entities.udfverify.UsingMapEntry; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| /** |
| * Test the Java UDF byte code verifier. |
| */ |
| public class UFVerifierTest extends CQLTester |
| { |
| @Test |
| public void testByteCodeVerifier() |
| { |
| verify(GoodClass.class); |
| } |
| |
| @Test |
| public void testClassWithField() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("field declared: field")), |
| verify(ClassWithField.class)); |
| } |
| |
| @Test |
| public void testClassWithInitializer() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("field declared: field", |
| "initializer declared")), |
| verify(ClassWithInitializer.class)); |
| } |
| |
| @Test |
| public void testClassWithInitializer2() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("field declared: field", |
| "initializer declared")), |
| verify(ClassWithInitializer2.class)); |
| } |
| |
| @Test |
| public void testClassWithInitializer3() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("initializer declared")), |
| verify(ClassWithInitializer3.class)); |
| } |
| |
| @Test |
| public void testClassWithStaticInitializer() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("static initializer declared")), |
| verify(ClassWithStaticInitializer.class)); |
| } |
| |
| @Test |
| public void testUseOfSynchronized() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("use of synchronized")), |
| verify(UseOfSynchronized.class)); |
| } |
| |
| @Test |
| public void testUseOfSynchronizedWithNotify() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("use of synchronized", "call to java.lang.Object.notify()")), |
| verify(UseOfSynchronizedWithNotify.class)); |
| } |
| |
| @Test |
| public void testUseOfSynchronizedWithNotifyAll() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("use of synchronized", "call to java.lang.Object.notifyAll()")), |
| verify(UseOfSynchronizedWithNotifyAll.class)); |
| } |
| |
| @Test |
| public void testUseOfSynchronizedWithWait() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("use of synchronized", "call to java.lang.Object.wait()")), |
| verify(UseOfSynchronizedWithWait.class)); |
| } |
| |
| @Test |
| public void testUseOfSynchronizedWithWaitL() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("use of synchronized", "call to java.lang.Object.wait()")), |
| verify(UseOfSynchronizedWithWaitL.class)); |
| } |
| |
| @Test |
| public void testUseOfSynchronizedWithWaitI() |
| { |
| assertEquals(new HashSet<>(Arrays.asList("use of synchronized", "call to java.lang.Object.wait()")), |
| verify(UseOfSynchronizedWithWaitLI.class)); |
| } |
| |
| @Test |
| public void testCallClone() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("call to java.lang.Object.clone()")), |
| verify(CallClone.class)); |
| } |
| |
| @Test |
| public void testCallFinalize() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("call to java.lang.Object.finalize()")), |
| verify(CallFinalize.class)); |
| } |
| |
| @Test |
| public void testCallComDatastax() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("call to com.datastax.driver.core.DataType.cint()")), |
| verify("com/", CallComDatastax.class)); |
| } |
| |
| @Test |
| public void testCallOrgApache() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("call to org.apache.cassandra.config.DatabaseDescriptor.getClusterName()")), |
| verify("org/", CallOrgApache.class)); |
| } |
| |
| @Test |
| public void testClassStaticInnerClass() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("class declared as inner class")), |
| verify(ClassWithStaticInnerClass.class)); |
| } |
| |
| @Test |
| public void testUsingMapEntry() |
| { |
| assertEquals(Collections.emptySet(), |
| verify(UsingMapEntry.class)); |
| } |
| |
| @Test |
| public void testClassInnerClass() |
| { |
| assertEquals(new HashSet<>(Collections.singletonList("class declared as inner class")), |
| verify(ClassWithInnerClass.class)); |
| } |
| |
| @Test |
| public void testClassInnerClass2() |
| { |
| assertEquals(Collections.emptySet(), |
| verify(ClassWithInnerClass2.class)); |
| } |
| |
| private Set<String> verify(Class cls) |
| { |
| return new UDFByteCodeVerifier().verify(cls.getName(), readClass(cls)); |
| } |
| |
| private Set<String> verify(String disallowedPkg, Class cls) |
| { |
| return new UDFByteCodeVerifier().addDisallowedPackage(disallowedPkg).verify(cls.getName(), readClass(cls)); |
| } |
| |
| @SuppressWarnings("resource") |
| private static byte[] readClass(Class<?> clazz) |
| { |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| URL res = clazz.getClassLoader().getResource(clazz.getName().replace('.', '/') + ".class"); |
| assert res != null; |
| try (InputStream input = res.openConnection().getInputStream()) |
| { |
| int i; |
| while ((i = input.read()) != -1) |
| out.write(i); |
| return out.toByteArray(); |
| } |
| catch (IOException e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Test |
| public void testInvalidByteCodeUDFs() throws Throwable |
| { |
| assertInvalidByteCode("try\n" + |
| "{\n" + |
| " clone();\n" + |
| "}\n" + |
| "catch (CloneNotSupportedException e)\n" + |
| "{\n" + |
| " throw new RuntimeException(e);\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.clone()]"); |
| assertInvalidByteCode("try\n" + |
| "{\n" + |
| " finalize();\n" + |
| "}\n" + |
| "catch (Throwable e)\n" + |
| "{\n" + |
| " throw new RuntimeException(e);\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.finalize()]"); |
| assertInvalidByteCode('\n' + |
| "return 0d;\n" + |
| " }\n" + |
| '\n' + |
| " Object field;\n" + |
| '\n' + |
| " {", "Java UDF validation failed: [field declared: field]"); |
| assertInvalidByteCode('\n' + |
| "return 0d;\n" + |
| " }\n" + |
| '\n' + |
| " final Object field;\n" + |
| '\n' + |
| " {\n" + |
| "field = new Object();", "Java UDF validation failed: [field declared: field, initializer declared]"); |
| assertInvalidByteCode('\n' + |
| "return 0d;\n" + |
| " }\n" + |
| '\n' + |
| " Object field = new Object();\n" + |
| '\n' + |
| " {\n" + |
| "Math.sin(1d);", "Java UDF validation failed: [field declared: field, initializer declared]"); |
| assertInvalidByteCode('\n' + |
| "return 0d;\n" + |
| " }\n" + |
| '\n' + |
| " {\n" + |
| "Math.sin(1d);", "Java UDF validation failed: [initializer declared]"); |
| assertInvalidByteCode('\n' + |
| "return 0d;\n" + |
| " }\n" + |
| '\n' + |
| " static\n" + |
| " {\n" + |
| "Math.sin(1d);", "Java UDF validation failed: [static initializer declared]"); |
| assertInvalidByteCode("synchronized (this)\n" + |
| "{\n" + |
| " Math.sin(1d);\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [use of synchronized]"); |
| assertInvalidByteCode("synchronized (this)\n" + |
| "{\n" + |
| " notify();\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.notify(), use of synchronized]"); |
| assertInvalidByteCode("synchronized (this)\n" + |
| "{\n" + |
| " notifyAll();\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.notifyAll(), use of synchronized]"); |
| assertInvalidByteCode("synchronized (this)\n" + |
| "{\n" + |
| " try\n" + |
| " {\n" + |
| " wait();\n" + |
| " }\n" + |
| " catch (InterruptedException e)\n" + |
| " {\n" + |
| " throw new RuntimeException(e);\n" + |
| " }\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.wait(), use of synchronized]"); |
| assertInvalidByteCode("synchronized (this)\n" + |
| "{\n" + |
| " try\n" + |
| " {\n" + |
| " wait(1000L);\n" + |
| " }\n" + |
| " catch (InterruptedException e)\n" + |
| " {\n" + |
| " throw new RuntimeException(e);\n" + |
| " }\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.wait(), use of synchronized]"); |
| assertInvalidByteCode("synchronized (this)\n" + |
| "{\n" + |
| " try\n" + |
| " {\n" + |
| " wait(1000L, 100);\n" + |
| " }\n" + |
| " catch (InterruptedException e)\n" + |
| " {\n" + |
| " throw new RuntimeException(e);\n" + |
| " }\n" + |
| "}\n" + |
| "return 0d;", "Java UDF validation failed: [call to java.lang.Object.wait(), use of synchronized]"); |
| assertInvalidByteCode("try {" + |
| " java.nio.ByteBuffer.allocateDirect(123); return 0d;" + |
| "} catch (Exception t) {" + |
| " throw new RuntimeException(t);" + |
| '}', "Java UDF validation failed: [call to java.nio.ByteBuffer.allocateDirect()]"); |
| assertInvalidByteCode("try {" + |
| " java.net.InetAddress.getLocalHost(); return 0d;" + |
| "} catch (Exception t) {" + |
| " throw new RuntimeException(t);" + |
| '}', "Java UDF validation failed: [call to java.net.InetAddress.getLocalHost()]"); |
| assertInvalidByteCode("try {" + |
| " java.net.InetAddress.getAllByName(\"localhost\"); return 0d;" + |
| "} catch (Exception t) {" + |
| " throw new RuntimeException(t);" + |
| '}', "Java UDF validation failed: [call to java.net.InetAddress.getAllByName()]"); |
| assertInvalidByteCode("try {" + |
| " java.net.Inet4Address.getByName(\"127.0.0.1\"); return 0d;" + |
| "} catch (Exception t) {" + |
| " throw new RuntimeException(t);" + |
| '}', "Java UDF validation failed: [call to java.net.Inet4Address.getByName()]"); |
| assertInvalidByteCode("try {" + |
| " java.net.Inet6Address.getByAddress(new byte[]{127,0,0,1}); return 0d;" + |
| "} catch (Exception t) {" + |
| " throw new RuntimeException(t);" + |
| '}', "Java UDF validation failed: [call to java.net.Inet6Address.getByAddress()]"); |
| assertInvalidByteCode("try {" + |
| " java.net.NetworkInterface.getNetworkInterfaces(); return 0d;" + |
| "} catch (Exception t) {" + |
| " throw new RuntimeException(t);" + |
| '}', "Java UDF validation failed: [call to java.net.NetworkInterface.getNetworkInterfaces()]"); |
| } |
| |
| private void assertInvalidByteCode(String body, String error) throws Throwable |
| { |
| assertInvalidMessage(error, |
| "CREATE FUNCTION " + KEYSPACE + ".mustBeInvalid ( input double ) " + |
| "CALLED ON NULL INPUT " + |
| "RETURNS double " + |
| "LANGUAGE java AS $$" + body + "$$"); |
| } |
| } |