Skip to content

Commit f469eef

Browse files
committed
Implement allowlisting for methods and classes
1 parent 8ba78b9 commit f469eef

1 file changed

Lines changed: 55 additions & 6 deletions

File tree

src/main/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolver.java

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.hubspot.jinjava.el.ext;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import com.google.common.base.CaseFormat;
45
import com.google.common.collect.ImmutableSet;
56
import com.hubspot.jinjava.interpret.DeferredValueException;
@@ -26,6 +27,11 @@ public class JinjavaBeanELResolver extends BeanELResolver {
2627
.add("class")
2728
.build();
2829

30+
// These aren't required, but they prevent someone from misconfiguring Jinjava to allow sandbox bypass unintentionally
31+
private static final String JAVA_LANG_REFLECT_PACKAGE =
32+
Method.class.getPackage().getName(); // java.lang.reflect
33+
private static final String JACKSON_DATABIND_PACKAGE =
34+
ObjectMapper.class.getPackage().getName(); // com.fasterxml.jackson.databind
2935
private static final Set<String> DEFAULT_RESTRICTED_METHODS = ImmutableSet
3036
.<String>builder()
3137
.add("class")
@@ -64,13 +70,46 @@ public class JinjavaBeanELResolver extends BeanELResolver {
6470

6571
@Value.Immutable(singleton = true)
6672
public interface JinjavaBeanELResolverConfig {
67-
ImmutableSet<Method> allowListedMethods();
73+
ImmutableSet<Method> allowMethods();
74+
ImmutableSet<Class<?>> allowDeclaredMethodsFromClasses();
6875

6976
@Value.Default
7077
default boolean readOnly() {
7178
return false;
7279
}
7380

81+
@Value.Check
82+
default void banClassesAndMethods() {
83+
if (
84+
allowMethods()
85+
.stream()
86+
.anyMatch(method ->
87+
Class.class.equals(method.getDeclaringClass()) ||
88+
Object.class.equals(method.getDeclaringClass()) ||
89+
method.getDeclaringClass().getName().startsWith(JAVA_LANG_REFLECT_PACKAGE) ||
90+
method.getDeclaringClass().getName().startsWith(JACKSON_DATABIND_PACKAGE)
91+
)
92+
) {
93+
throw new IllegalStateException(
94+
"Methods from banned classes (Object.class, Class.class) are not allowed"
95+
);
96+
}
97+
if (
98+
allowDeclaredMethodsFromClasses()
99+
.stream()
100+
.anyMatch(clazz ->
101+
Class.class.equals(clazz) ||
102+
Object.class.equals(clazz) ||
103+
clazz.getName().startsWith(JAVA_LANG_REFLECT_PACKAGE) ||
104+
clazz.getName().startsWith(JACKSON_DATABIND_PACKAGE)
105+
)
106+
) {
107+
throw new IllegalStateException(
108+
"Banned classes (Object.class, Class.class) are not allowed"
109+
);
110+
}
111+
}
112+
74113
static JinjavaBeanELResolverConfig.Builder builder() {
75114
return new Builder();
76115
}
@@ -182,7 +221,10 @@ protected Method findMethod(
182221
List<Method> potentialMethods = new LinkedList<>();
183222

184223
for (Method m : methods) {
185-
if (m.getName().equals(name)) {
224+
if (
225+
m.getName().equals(name) &&
226+
(isDeclaringClassAllowed(m.getDeclaringClass()) || methodAllowed(m))
227+
) {
186228
int formalParamCount = m.getParameterTypes().length;
187229
if (m.isVarArgs() && paramCount >= formalParamCount - 1) {
188230
varArgsMethod = m;
@@ -208,10 +250,17 @@ protected Method findMethod(
208250
)
209251
);
210252
}
211-
if (true || jinjavaBeanELResolverConfig.allowListedMethods().contains(method)) {
212-
return method;
213-
}
214-
return null;
253+
return method;
254+
}
255+
256+
private boolean methodAllowed(Method m) {
257+
return jinjavaBeanELResolverConfig.allowMethods().contains(m);
258+
}
259+
260+
private boolean isDeclaringClassAllowed(Class<?> declaringClass) {
261+
return jinjavaBeanELResolverConfig
262+
.allowDeclaredMethodsFromClasses()
263+
.contains(declaringClass);
215264
}
216265

217266
private static boolean checkAssignableParameterTypes(Object[] params, Method method) {

0 commit comments

Comments
 (0)