package io.shockah.skylark.groovy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.github.kevinsawicki.http.HttpRequest;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import groovy.lang.Tuple;
import groovy.lang.Tuple2;
public class GroovySandboxImpl extends AbstractGroovySandbox {
private static final List<Class<?>> CLASS_BLACKLIST = ImmutableList.<Class<?>>builder().add(
System.class
).build();
private static final Map<Class<?>, List<String>> METHOD_BLACKLIST = ImmutableMap.<Class<?>, List<String>>builder().put(
Object.class, ImmutableList.<String>builder().add(
"wait", "notify", "notifyAll", "finalize"
).build()
).put(
Class.class, ImmutableList.<String>builder().add(
"getClassLoader", "getResource", "newInstance"
).build()
).put(
Constructor.class, ImmutableList.<String>builder().add(
"newInstance"
).build()
).put(
Method.class, ImmutableList.<String>builder().add(
"invoke"
).build()
).put(
Field.class, ImmutableList.<String>builder().add(
"get", "getBoolean", "getByte", "getChar", "getDouble", "getFloat", "getInt", "getLong", "getShort",
"set", "setBoolean", "setByte", "setChar", "setDouble", "setFloat", "setInt", "setLong", "setShort"
).build()
).build();
private static final Map<Class<?>, List<String>> FIELD_BLACKLIST = ImmutableMap.<Class<?>, List<String>>builder().build();
private static final List<String> PACKAGE_WHITELIST = ImmutableList.<String>builder().add(
"io.shockah.json",
"java.math", "java.text", "java.util",
"groovy.json"
).build();
private static final List<Class<?>> CLASS_WHITELIST = ImmutableList.<Class<?>>builder().add(
Boolean.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class,
Character.class, String.class, Object.class, Number.class, StringBuilder.class, Math.class,
HttpRequest.class, Class.class,
Tuple.class, Tuple2.class
).build();
private static final Map<Class<?>, List<String>> METHOD_WHITELIST = ImmutableMap.<Class<?>, List<String>>builder().build();
protected final List<Class<?>> classBlacklist = new ArrayList<>(CLASS_BLACKLIST);
protected final Map<Class<?>, List<String>> methodBlacklist = new HashMap<>(METHOD_BLACKLIST);
protected final Map<Class<?>, List<String>> fieldBlacklist = new HashMap<>(FIELD_BLACKLIST);
protected final List<String> packageWhitelist = new ArrayList<>(PACKAGE_WHITELIST);
protected final List<Class<?>> classWhitelist = new ArrayList<>(CLASS_WHITELIST);
protected final Map<Class<?>, List<String>> methodWhitelist = new HashMap<>(METHOD_WHITELIST);
protected final boolean isClassBlacklisted(Class<?> clazz) {
do {
if (classBlacklist.contains(clazz))
return true;
clazz = clazz.getSuperclass();
} while (clazz != null);
return false;
}
protected final boolean isMethodBlacklisted(Class<?> clazz, String method) {
do {
List<String> methods = methodBlacklist.get(clazz);
if (methods != null && methods.contains(method))
return true;
clazz = clazz.getSuperclass();
} while (clazz != null);
return false;
}
protected final boolean isFieldBlacklisted(Class<?> clazz, String field) {
do {
List<String> fields = fieldBlacklist.get(clazz);
if (fields != null && fields.contains(field))
return true;
clazz = clazz.getSuperclass();
} while (clazz != null);
return false;
}
protected final boolean isPackageWhitelisted(Class<?> clazz) {
for (String packagePrefix : packageWhitelist) {
if (clazz.getName().startsWith(packagePrefix + "."))
return true;
}
return false;
}
protected final boolean isClassWhitelisted(Class<?> clazz) {
return classWhitelist.contains(clazz);
/*do {
if (classWhitelist.contains(clazz))
return true;
clazz = clazz.getSuperclass();
} while (clazz != null);
return false;*/
}
protected final Boolean isMethodWhitelisted(Class<?> clazz, String method) {
do {
List<String> methods = methodWhitelist.get(clazz);
if (methods != null) {
boolean drop = false;
for (Method classMethod : clazz.getDeclaredMethods()) {
if (classMethod.getName().equals(method)) {
if (methods.contains(method))
return true;
else
drop = true;
}
}
if (drop)
return false;
}
clazz = clazz.getSuperclass();
} while (clazz != null);
return null;
}
@Override
public boolean isInstanceMethodCallAllowed(Object obj, String method, Object... args) {
if (isClassBlacklisted(obj.getClass()))
return false;
if (isMethodBlacklisted(obj.getClass(), method))
return false;
if (isPackageWhitelisted(obj.getClass()))
return true;
if (isClassWhitelisted(obj.getClass()))
return true;
Boolean methodWhitelisted = isMethodWhitelisted(obj.getClass(), method);
if (methodWhitelisted != null)
return methodWhitelisted;
return true;
}
@Override
public boolean isClassMethodCallAllowed(Class<?> clazz, String method, Object... args) {
if (isClassBlacklisted(clazz))
return false;
if (isMethodBlacklisted(clazz, method))
return false;
if (isPackageWhitelisted(clazz))
return true;
if (isClassWhitelisted(clazz))
return true;
Boolean methodWhitelisted = isMethodWhitelisted(clazz, method);
if (methodWhitelisted != null)
return methodWhitelisted;
return false;
}
@Override
public boolean isConstructorAllowed(Class<?> clazz, Object... args) {
if (isClassBlacklisted(clazz))
return false;
if (isPackageWhitelisted(clazz))
return true;
if (isClassWhitelisted(clazz))
return true;
return false;
}
@Override
public boolean isInstanceFieldGetAllowed(Object obj, String field) {
if (isClassBlacklisted(obj.getClass()))
return false;
if (isFieldBlacklisted(obj.getClass(), field))
return false;
/*if (isPackageWhitelisted(obj.getClass()))
return true;
if (isClassWhitelisted(obj.getClass()))
return true;*/
return true;
}
@Override
public boolean isInstanceFieldSetAllowed(Object obj, String field, Object value) {
if (isClassBlacklisted(obj.getClass()))
return false;
if (isFieldBlacklisted(obj.getClass(), field))
return false;
/*if (isPackageWhitelisted(obj.getClass()))
return true;
if (isClassWhitelisted(obj.getClass()))
return true;*/
return true;
}
@Override
public boolean isClassFieldGetAllowed(Class<?> clazz, String field) {
if (isClassBlacklisted(clazz))
return false;
/*if (isPackageWhitelisted(clazz))
return true;
if (isClassWhitelisted(clazz))
return true;*/
return true;
}
@Override
public boolean isClassFieldSetAllowed(Class<?> clazz, String field, Object value) {
if (isClassBlacklisted(clazz))
return false;
/*if (isPackageWhitelisted(clazz))
return true;
if (isClassWhitelisted(clazz))
return true;*/
return true;
}
public GroovySandboxImpl addBlacklistedClasses(Class<?>... classes) {
for (Class<?> clazz : classes)
classBlacklist.add(clazz);
return this;
}
public GroovySandboxImpl addBlacklistedMethods(Class<?> clazz, String... methods) {
List<String> list = methodBlacklist.get(clazz);
if (list == null) {
list = new ArrayList<>();
methodBlacklist.put(clazz, list);
}
for (String method : methods)
list.add(method);
return this;
}
public GroovySandboxImpl addBlacklistedFields(Class<?> clazz, String... fields) {
List<String> list = fieldBlacklist.get(clazz);
if (list == null) {
list = new ArrayList<>();
fieldBlacklist.put(clazz, list);
}
for (String field : fields)
list.add(field);
return this;
}
public GroovySandboxImpl addWhitelistedPackages(String... packages) {
for (String pack : packages)
packageWhitelist.add(pack);
return this;
}
public GroovySandboxImpl addWhitelistedClasses(Class<?>... classes) {
for (Class<?> clazz : classes)
classWhitelist.add(clazz);
return this;
}
public GroovySandboxImpl addWhitelistedMethods(Class<?> clazz, String... methods) {
List<String> list = methodWhitelist.get(clazz);
if (list == null) {
list = new ArrayList<>();
methodWhitelist.put(clazz, list);
}
for (String method : methods)
list.add(method);
return this;
}
}