/* * Copyright 2011 Google Inc. * * Licensed 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 com.google.gwt.user.server.rpc; import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializedTypeViolationException; import junit.framework.TestCase; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; /** * Array test cases for the type checking code found in server-side RPC * deserialization. */ @SuppressWarnings("rawtypes") public class RPCTypeCheckArraysTest extends TestCase { /* * Test that primitive arrays cannot be replaced by non-array types * - int for int[]: testPrimitiveSpoofingArray * - String for String[]: testPrimitiveSpoofingArray * - HashSet for int[]: testHashSetSpoofingArray * - HashSet for String[]: testHashSetSpoofingArray * - HashSet for AClass[]: testHashSetSpoofingClassArray * * Test that non-array-type cannot be replaced by array types * - int[] for int: testArraysSpoofingObjects * - String[] for String: testArraytestArraysSpoofingObjects * - String[] for AClass: testArraysSpoofingObjects * * Test generics parameterized by arrays: * - HashMap<String, int[]>: testArrayAsGeneric * - HashMap<String, Integer[]>: testArrayAsGeneric * - HashMap<String, AClass[]>: testArrayAsGeneric * * Test arrays of generics: * - List<Integer>[]: testArrayOfGeneric * - List[]: testArrayOfGeneric * - List<? extends HashSet<Integer>>[]: testArrayOfGeneric */ /** * A class for testing spoofing of arrays. */ public static class ArraysParamTestClass implements RemoteService { @SuppressWarnings("unused") public static void testArrays(int[] intArg, String[] stringArg) { } @SuppressWarnings("unused") public static void testAClassArray(RPCTypeCheckTest.AClass[] arg1) { } @SuppressWarnings("unused") public static void testGenericsArrays( HashMap<String, int[]> arg1, HashMap<String, Integer[]> arg2, HashMap<String, RPCTypeCheckTest.AClass[]> arg3) { } @SuppressWarnings("unused") public static void testArrayOfGeneric(List<Integer>[] arg1) { } @SuppressWarnings("unused") public static void testArrayOfGenericRaw(List[] arg1) { } @SuppressWarnings("unused") public static void testArrayOfGenericWildcard(List<? extends HashSet<Integer>>[] arg1) { } } private static String generateArrayGenericsA() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testGenericsArrays"); HashMap<String, HashSet> arg1 = new HashMap<String, HashSet>(); arg1.put("foo", RPCTypeCheckFactory.generateTestHashSet()); strFactory.write(arg1); HashMap<String, Integer[]> arg2 = new HashMap<String, Integer[]>(); Integer[] entry1 = new Integer[] {12345, 67890}; arg2.put("bar", entry1); strFactory.write(arg2); HashMap<String, RPCTypeCheckTest.AClass[]> arg3 = new HashMap<String, RPCTypeCheckTest.AClass[]>(); RPCTypeCheckTest.AClass[] entry2 = new RPCTypeCheckTest.AClass[2]; entry2[0] = new RPCTypeCheckTest.AClass(234); entry2[1] = new RPCTypeCheckTest.AClass(567); arg3.put("baz", entry2); strFactory.write(arg3); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateArrayGenericsB() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testGenericsArrays"); HashMap<String, int[]> arg1 = new HashMap<String, int[]>(); int[] entry1 = new int[] { 234, 567 }; arg1.put("foo", entry1); strFactory.write(arg1); HashMap<String, HashSet[]> arg2 = new HashMap<String, HashSet[]>(); HashSet[] entry2 = new HashSet[] { RPCTypeCheckFactory.generateTestHashSet() }; arg2.put("bar", entry2); strFactory.write(arg2); HashMap<String, RPCTypeCheckTest.AClass[]> arg3 = new HashMap<String, RPCTypeCheckTest.AClass[]>(); RPCTypeCheckTest.AClass[] entry3 = new RPCTypeCheckTest.AClass[2]; entry3[0] = new RPCTypeCheckTest.AClass(234); entry3[1] = new RPCTypeCheckTest.AClass(567); arg3.put("baz", entry3); strFactory.write(arg3); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateArrayGenericsC() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testGenericsArrays"); HashMap<String, int[]> arg1 = new HashMap<String, int[]>(); int[] entry1 = new int[] { 234, 567 }; arg1.put("foo", entry1); strFactory.write(arg1); HashMap<String, Integer[]> arg2 = new HashMap<String, Integer[]>(); Integer[] entry2 = new Integer[] {12345, 67890}; arg2.put("bar", entry2); strFactory.write(arg2); HashMap<String, HashSet[]> arg3 = new HashMap<String, HashSet[]>(); HashSet[] entry3 = new HashSet[] { RPCTypeCheckFactory.generateTestHashSet() }; arg3.put("baz", entry3); strFactory.write(arg3); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateArrayOfGenericsA(String methodName) { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, methodName); HashMap<String, HashSet> arg1 = new HashMap<String, HashSet>(); arg1.put("foo", RPCTypeCheckFactory.generateTestHashSet()); strFactory.write(arg1); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateArrayOfGenericsB(String methodName) { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, methodName); LinkedList[] arg1 = new LinkedList[1]; LinkedList<LinkedHashSet> list = new LinkedList<LinkedHashSet>(); list.add(RPCTypeCheckFactory.generateTestLinkedHashSet()); arg1[0] = list; strFactory.write(arg1); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } @SuppressWarnings("unchecked") private static String generateArrayOfGenericsC(String methodName) { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, methodName); LinkedList<HashSet<Integer>>[] arg1 = new LinkedList[1]; LinkedList<HashSet<Integer>> list = new LinkedList<HashSet<Integer>>(); HashSet<Integer> listElmt = new HashSet<Integer>(); listElmt.add(12345); listElmt.add(67890); list.add(listElmt); arg1[0] = list; strFactory.write(arg1); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateHashSetSpoofingClassArray() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testAClassArray"); HashSet<Integer> hashSet = new HashSet<Integer>(); hashSet.add(12345); hashSet.add(67890); strFactory.write(hashSet); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateHashSetSpoofingIntArray() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays"); HashSet<Integer> hashSet = new HashSet<Integer>(); hashSet.add(12345); hashSet.add(67890); strFactory.write(hashSet); String[] stringArray = new String[]{"foo", "bar"}; strFactory.write(stringArray); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateHashSetSpoofingStringArray() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays"); int[] intArray = new int[]{12345, 67890}; strFactory.write(intArray); HashSet<Integer> hashSet = new HashSet<Integer>(); hashSet.add(12345); hashSet.add(67890); strFactory.write(hashSet); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateIntArraySpoofingInt() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(RPCTypeCheckTest.PrimitiveParamTestClass.class, "testIntString"); int[] iArray = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; strFactory.write(iArray); strFactory.write("foo"); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateIntSpoofingIntArray() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays"); int i = 12345; strFactory.write(i); String[] stringArray = new String[]{"foo", "bar"}; strFactory.write(stringArray); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateStringArraySpoofingClass() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(RPCTypeCheckTest.ClassesParamTestClass.class, "testAClass"); String a = "a"; String[] stringArray = new String[]{a, a, a, a, a, a, a, a, a, a, a, a, a, a}; strFactory.write(stringArray); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateStringArraySpoofingString() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(RPCTypeCheckTest.PrimitiveParamTestClass.class, "testIntString"); int i = 12345; strFactory.write(i); String a = "a"; String[] stringArray = new String[]{a, a, a, a, a, a, a, a, a, a, a, a, a, a}; strFactory.write(stringArray); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } private static String generateStringSpoofingStringArray() { try { RPCTypeCheckFactory strFactory = new RPCTypeCheckFactory(ArraysParamTestClass.class, "testArrays"); int[] arg1 = new int[]{12345, 67890}; strFactory.write(arg1); String arg2 = "bar"; strFactory.write(arg2); return strFactory.toString(); } catch (Exception e) { fail(e.getMessage()); return null; } } /** * This checks that arrays as generic types are checked correctly. */ public void testArrayGenerics() { try { RPC.decodeRequest(generateArrayGenericsA()); fail("Expected IncompatibleRemoteServiceException from testArrayGenerics (1)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*int.*")); } try { RPC.decodeRequest(generateArrayGenericsB()); fail("Expected IncompatibleRemoteServiceException from testArrayGenerics (2)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*")); } try { RPC.decodeRequest(generateArrayGenericsC()); fail("Expected IncompatibleRemoteServiceException from testArrayGenerics (3)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*AClass.*")); } } /** * This checks that arrays of generic types are checked correctly. */ public void testArrayOfGenerics() { try { RPC.decodeRequest(generateArrayOfGenericsC("testArrayOfGenericRaw")); // Expect to pass } catch (Exception e) { fail("Unexpected exception from testArrayOfGenerics (0): " + e.getMessage()); } try { RPC.decodeRequest(generateArrayOfGenericsA("testArrayOfGeneric")); fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (1)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashMap.*List.*")); } try { RPC.decodeRequest(generateArrayOfGenericsB("testArrayOfGeneric")); fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (2)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*")); } try { RPC.decodeRequest(generateArrayOfGenericsA("testArrayOfGenericWildcard")); fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (3)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashMap.*List.*")); } try { RPC.decodeRequest(generateArrayOfGenericsC("testArrayOfGenericWildcard")); } catch (Exception e) { fail("Unexpected Exception from testArrayOfGenerics (4): " + e.getMessage()); } try { RPC.decodeRequest(generateArrayOfGenericsB("testArrayOfGenericWildcard")); fail("Expected IncompatibleRemoteServiceException from testArrayOfGenerics (5)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*Integer.*")); } } /** * This checks that arrays cannot be used in place of primitives. */ public void testArraysSpoofingObjects() { try { RPC.decodeRequest(generateIntArraySpoofingInt()); fail("Expected ArrayIndexOutOfBoundsException from testArraysSpoofingPrimitives (1)"); } catch (Exception e) { // Expected to get here assertTrue(e instanceof ArrayIndexOutOfBoundsException); } try { RPCRequest result = RPC.decodeRequest(generateStringArraySpoofingString()); assertTrue(result.getParameters()[1].toString().matches(".*\\[Ljava.lang.String.*")); } catch (Exception e) { fail("Unexpected Exception from testArraysSpoofingPrimitives (2)"); } try { RPC.decodeRequest(generateStringArraySpoofingClass()); fail("Expected IncompatibleRemoteServiceException from testArraysSpoofingPrimitives (3)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*\\[Ljava.lang.String.*AClass.*")); } } /** * This checks situations in which an RPC message is modified to replace * arguments of a primitive type with another primitive type. */ public void testHashSetSpoofingArray() { try { RPC.decodeRequest(generateHashSetSpoofingIntArray()); fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingArray (1)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here. When the int array is processed, it reads the // HashSet class and fails to know what it is, because it is not the // way an int would be serialized in this context. assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*\\[I.*")); } try { RPC.decodeRequest(generateHashSetSpoofingStringArray()); fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingArray (2)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*String.*")); } } /** * This checks situations in which an RPC message is modified to replace an * array of objects with a HashSet (custom serialized container type). */ public void testHashSetSpoofingClassArray() { try { RPC.decodeRequest(generateHashSetSpoofingClassArray()); fail("Expected IncompatibleRemoteServiceException from testHashSetSpoofingClassArray"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here. assertEquals(SerializedTypeViolationException.class, e.getCause().getClass()); assertTrue(e.getCause().getMessage().matches(".*HashSet.*AClass.*")); } } /** * This checks situations in which an RPC message is modified to replace * arguments of a primitive type with another primitive type. */ public void testPrimitiveSpoofingArray() { try { RPC.decodeRequest(generateIntSpoofingIntArray()); fail("Expected IncompatibleRemoteServiceException from testPrimitiveSpoofingArray (1)"); } catch (Exception e) { // Expected to get here. When the int array value is processed, it reads // the // integer value and tries to look it up as a class string, only to run // out // of bounds in the string table. assertTrue(e instanceof ArrayIndexOutOfBoundsException); } try { RPC.decodeRequest(generateStringSpoofingStringArray()); fail("Expected IncompatibleRemoteServiceException from testPrimitiveSpoofingArray (2)"); } catch (IncompatibleRemoteServiceException e) { // Expected to get here assertEquals(SerializationException.class, e.getCause().getClass()); } } }