Skip to content

Commit 9140460

Browse files
committed
Add back test for Security Manager
1 parent 2dfccd2 commit 9140460

3 files changed

Lines changed: 117 additions & 28 deletions

File tree

‎gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java‎

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.gson.internal.bind;
1818

1919
import java.io.IOException;
20+
import java.lang.reflect.AccessibleObject;
2021
import java.lang.reflect.Field;
2122
import java.math.BigDecimal;
2223
import java.math.BigInteger;
@@ -759,22 +760,31 @@ private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapte
759760
private final Map<String, T> nameToConstant = new HashMap<String, T>();
760761
private final Map<T, String> constantToName = new HashMap<T, String>();
761762

762-
public EnumTypeAdapter(Class<T> classOfT) {
763+
public EnumTypeAdapter(final Class<T> classOfT) {
763764
try {
764-
for (final Field field : classOfT.getDeclaredFields()) {
765-
if (!field.isEnumConstant()) {
766-
continue;
767-
}
768-
AccessController.doPrivileged(new PrivilegedAction<Void>() {
769-
@Override public Void run() {
770-
field.setAccessible(true);
771-
return null;
765+
// Uses reflection to find enum constants to work around name mismatches for obfuscated classes
766+
// Reflection access might throw SecurityException, therefore run this in privileged context;
767+
// should be acceptable because this only retrieves enum constants, but does not expose anything else
768+
Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() {
769+
@Override public Field[] run() {
770+
Field[] fields = classOfT.getDeclaredFields();
771+
ArrayList<Field> constantFieldsList = new ArrayList<Field>(fields.length);
772+
for (Field f : fields) {
773+
if (f.isEnumConstant()) {
774+
constantFieldsList.add(f);
775+
}
772776
}
773-
});
777+
778+
Field[] constantFields = constantFieldsList.toArray(new Field[0]);
779+
AccessibleObject.setAccessible(constantFields, true);
780+
return constantFields;
781+
}
782+
});
783+
for (Field constantField : constantFields) {
774784
@SuppressWarnings("unchecked")
775-
T constant = (T)(field.get(null));
785+
T constant = (T)(constantField.get(null));
776786
String name = constant.name();
777-
SerializedName annotation = field.getAnnotation(SerializedName.class);
787+
SerializedName annotation = constantField.getAnnotation(SerializedName.class);
778788
if (annotation != null) {
779789
name = annotation.value();
780790
for (String alternate : annotation.alternate()) {

‎gson/src/test/java/com/google/gson/functional/EnumTest.java‎

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@
1616

1717
package com.google.gson.functional;
1818

19-
import java.lang.reflect.Type;
20-
import java.util.ArrayList;
21-
import java.util.Collection;
22-
import java.util.EnumSet;
23-
import java.util.Set;
24-
2519
import com.google.gson.Gson;
2620
import com.google.gson.GsonBuilder;
2721
import com.google.gson.JsonDeserializationContext;
@@ -34,7 +28,11 @@
3428
import com.google.gson.annotations.SerializedName;
3529
import com.google.gson.common.MoreAsserts;
3630
import com.google.gson.reflect.TypeToken;
37-
31+
import java.lang.reflect.Type;
32+
import java.util.ArrayList;
33+
import java.util.Collection;
34+
import java.util.EnumSet;
35+
import java.util.Set;
3836
import junit.framework.TestCase;
3937
/**
4038
* Functional tests for Java 5.0 enums.
@@ -200,17 +198,17 @@ public enum Gender {
200198
}
201199

202200
public void testEnumClassWithFields() {
203-
assertEquals("\"RED\"", gson.toJson(Color.RED));
204-
assertEquals("red", gson.fromJson("RED", Color.class).value);
201+
assertEquals("\"RED\"", gson.toJson(Color.RED));
202+
assertEquals("red", gson.fromJson("RED", Color.class).value);
205203
}
206204

207205
public enum Color {
208-
RED("red", 1), BLUE("blue", 2), GREEN("green", 3);
209-
String value;
210-
int index;
211-
private Color(String value, int index) {
212-
this.value = value;
213-
this.index = index;
214-
}
206+
RED("red", 1), BLUE("blue", 2), GREEN("green", 3);
207+
String value;
208+
int index;
209+
private Color(String value, int index) {
210+
this.value = value;
211+
this.index = index;
212+
}
215213
}
216214
}

‎gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,96 @@
11
package com.google.gson.functional;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNull;
45
import static org.junit.Assert.assertTrue;
56
import static org.junit.Assert.fail;
67

78
import com.google.gson.Gson;
9+
import com.google.gson.GsonBuilder;
810
import com.google.gson.JsonIOException;
11+
import com.google.gson.TypeAdapter;
12+
import com.google.gson.stream.JsonReader;
13+
import com.google.gson.stream.JsonWriter;
14+
import java.io.IOException;
15+
import java.lang.reflect.ReflectPermission;
16+
import java.net.URL;
17+
import java.net.URLClassLoader;
18+
import java.security.Permission;
919
import java.util.Collections;
20+
import java.util.concurrent.atomic.AtomicBoolean;
1021
import org.junit.Test;
1122

1223
public class ReflectionAccessTest {
24+
@SuppressWarnings("unused")
25+
private static class ClassWithPrivateMembers {
26+
private String s;
27+
28+
private ClassWithPrivateMembers() {
29+
}
30+
}
31+
32+
private static Class<?> loadClassWithDifferentClassLoader(Class<?> c) throws Exception {
33+
URL url = c.getProtectionDomain().getCodeSource().getLocation();
34+
URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, null);
35+
return classLoader.loadClass(c.getName());
36+
}
37+
38+
@Test
39+
public void testRestrictiveSecurityManager() throws Exception {
40+
// Must use separate class loader, otherwise permission is not checked, see Class.getDeclaredFields()
41+
Class<?> clazz = loadClassWithDifferentClassLoader(ClassWithPrivateMembers.class);
42+
43+
final Permission accessDeclaredMembers = new RuntimePermission("accessDeclaredMembers");
44+
final Permission suppressAccessChecks = new ReflectPermission("suppressAccessChecks");
45+
SecurityManager original = System.getSecurityManager();
46+
SecurityManager restrictiveManager = new SecurityManager() {
47+
@Override
48+
public void checkPermission(Permission perm) {
49+
if (accessDeclaredMembers.equals(perm)) {
50+
throw new SecurityException("Gson: no-member-access");
51+
}
52+
if (suppressAccessChecks.equals(perm)) {
53+
throw new SecurityException("Gson: no-suppress-access-check");
54+
}
55+
}
56+
};
57+
System.setSecurityManager(restrictiveManager);
58+
59+
try {
60+
Gson gson = new Gson();
61+
try {
62+
// Getting reflection based adapter should fail
63+
gson.getAdapter(clazz);
64+
fail();
65+
} catch (SecurityException e) {
66+
assertEquals("Gson: no-member-access", e.getMessage());
67+
}
68+
69+
final AtomicBoolean wasReadCalled = new AtomicBoolean(false);
70+
gson = new GsonBuilder()
71+
.registerTypeAdapter(clazz, new TypeAdapter<Object>() {
72+
@Override
73+
public void write(JsonWriter out, Object value) throws IOException {
74+
out.value("custom-write");
75+
}
76+
77+
@Override
78+
public Object read(JsonReader in) throws IOException {
79+
in.skipValue();
80+
wasReadCalled.set(true);
81+
return null;
82+
}}
83+
)
84+
.create();
85+
86+
assertEquals("\"custom-write\"", gson.toJson(null, clazz));
87+
assertNull(gson.fromJson("{}", clazz));
88+
assertTrue(wasReadCalled.get());
89+
} finally {
90+
System.setSecurityManager(original);
91+
}
92+
}
93+
1394
/**
1495
* Test serializing an instance of a non-accessible internal class, but where
1596
* Gson supports serializing one of its superinterfaces.

0 commit comments

Comments
 (0)