content: /*
   * 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.*;
  
  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 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));
      }
  
      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 + "$$");
      }
  }
  