Skip to content

Commit 3d02e50

Browse files
authored
Merge commit from fork
Prevent JinjavaBeanELResolver restriction bypassing through ForTag's loopVars
2 parents 098c895 + f5c4dd5 commit 3d02e50

File tree

5 files changed

+92
-3
lines changed

5 files changed

+92
-3
lines changed

CHANGES.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
# Jinjava Releases #
2+
### 2025-01-30 Version 2.8.3 ([Maven Central](https://search.maven.org/artifact/com.hubspot.jinjava/jinjava/2.8.3/jar)) ###
3+
* Disallow accessing properties on restricted classes while rendering through ForTag
4+
* Upgrade jackson to version 2.20
5+
* [Add performance optimization to chained filters](https://github.com/HubSpot/jinjava/pull/1274)
6+
7+
### 2025-01-30 Version 2.7.6 ([Maven Central](https://search.maven.org/artifact/com.hubspot.jinjava/jinjava/2.7.6/jar)) ###
8+
* Disallow accessing properties on restricted classes while rendering through ForTag
9+
* Upgrade jackson to version 2.20
10+
211
### 2025-10-22 Version 2.8.2 ([Maven Central](https://search.maven.org/artifact/com.hubspot.jinjava/jinjava/2.8.2/jar)) ###
312
* [Fix helper token escape handling and unescaping when unquoting strings](https://github.com/HubSpot/jinjava/pull/1263)
13+
14+
### 2025-09-30 Version 2.7.5 ([Maven Central](https://search.maven.org/artifact/com.hubspot.jinjava/jinjava/2.7.5/jar)) ###
15+
* Disallow accessing properties on restricted classes while rendering
16+
417
### 2025-09-16 Version 2.8.1 ([Maven Central](https://search.maven.org/artifact/com.hubspot.jinjava/jinjava/2.8.1/jar)) ###
518
* Disallow accessing properties on restricted classes while rendering
619
* [Make stack operations use AutoCloseable for safer usage with try-with-resources](https://github.com/HubSpot/jinjava/pull/1250)
20+
721
### 2025-05-05 Version 2.8.0 ([Maven Central](https://search.maven.org/artifact/com.hubspot.jinjava/jinjava/2.8.0/jar)) ###
822
* [Target Java 17](https://github.com/HubSpot/jinjava/pull/1238)
923
* [Implement PyMap#get with optional default](https://github.com/HubSpot/jinjava/pull/1233)

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

Lines changed: 10 additions & 2 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;
@@ -36,6 +37,7 @@ public class JinjavaBeanELResolver extends BeanELResolver {
3637
.add("notify")
3738
.add("notifyAll")
3839
.add("wait")
40+
.add("readValueAs")
3941
.build();
4042

4143
private static final Set<String> DEFERRED_EXECUTION_RESTRICTED_METHODS = ImmutableSet
@@ -59,6 +61,10 @@ public class JinjavaBeanELResolver extends BeanELResolver {
5961
.add("set")
6062
.add("merge")
6163
.build();
64+
private static final String JAVA_LANG_REFLECT_PACKAGE =
65+
Method.class.getPackage().getName(); // java.lang.reflect
66+
private static final String JACKSON_DATABIND_PACKAGE =
67+
ObjectMapper.class.getPackage().getName(); // com.fasterxml.jackson.databind
6268

6369
/**
6470
* Creates a new read/write {@link JinjavaBeanELResolver}.
@@ -251,9 +257,11 @@ protected boolean isRestrictedClass(Object o) {
251257
return false;
252258
}
253259

260+
Package oPackage = o.getClass().getPackage();
254261
return (
255-
(o.getClass().getPackage() != null &&
256-
o.getClass().getPackage().getName().startsWith("java.lang.reflect")) ||
262+
(oPackage != null &&
263+
(oPackage.getName().startsWith(JAVA_LANG_REFLECT_PACKAGE) ||
264+
oPackage.getName().startsWith(JACKSON_DATABIND_PACKAGE))) ||
257265
o instanceof Class ||
258266
o instanceof ClassLoader ||
259267
o instanceof Thread ||

src/main/java/com/hubspot/jinjava/lib/tag/ForTag.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public String renderForCollection(
247247
if (loopVar.equals(valProp.getName())) {
248248
interpreter
249249
.getContext()
250-
.put(loopVar, valProp.getReadMethod().invoke(val));
250+
.put(loopVar, interpreter.resolveProperty(val, valProp.getName()));
251251
break;
252252
}
253253
}

src/test/java/com/hubspot/jinjava/el/ext/JinjavaBeanELResolverTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
import static org.mockito.Mockito.mock;
66
import static org.mockito.Mockito.when;
77

8+
import com.fasterxml.jackson.databind.ObjectMapper;
89
import com.google.common.collect.ImmutableSet;
910
import com.hubspot.jinjava.JinjavaConfig;
1011
import com.hubspot.jinjava.el.JinjavaELContext;
1112
import com.hubspot.jinjava.interpret.AutoCloseableSupplier;
1213
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
14+
import java.util.List;
1315
import javax.el.ELContext;
1416
import javax.el.MethodNotFoundException;
1517
import javax.el.PropertyNotFoundException;
@@ -186,4 +188,60 @@ public void itDoesNotAllowAccessingPropertiesOfInterpreter() {
186188
.isNull();
187189
}
188190
}
191+
192+
@Test
193+
public void itDoesNotGettingFromObjectMapper() {
194+
try (
195+
AutoCloseableSupplier.AutoCloseableImpl<JinjavaInterpreter> c = JinjavaInterpreter
196+
.closeablePushCurrent(interpreter)
197+
.get()
198+
) {
199+
assertThat(
200+
jinjavaBeanELResolver.getValue(elContext, new ObjectMapper(), "dateFormat")
201+
)
202+
.isNull();
203+
}
204+
}
205+
206+
@Test
207+
public void itDoesNotAllowInvokingFromObjectMapper() throws NoSuchMethodException {
208+
try (
209+
AutoCloseableSupplier.AutoCloseableImpl<JinjavaInterpreter> c = JinjavaInterpreter
210+
.closeablePushCurrent(interpreter)
211+
.get()
212+
) {
213+
ObjectMapper objectMapper = new ObjectMapper();
214+
assertThatThrownBy(() ->
215+
jinjavaBeanELResolver.invoke(
216+
elContext,
217+
objectMapper,
218+
"getDateFormat",
219+
new Class[] {},
220+
new Object[] {}
221+
)
222+
)
223+
.isInstanceOf(MethodNotFoundException.class);
224+
}
225+
}
226+
227+
@Test
228+
public void itDoesNotAllowInvokingFromMethod() throws NoSuchMethodException {
229+
try (
230+
AutoCloseableSupplier.AutoCloseableImpl<JinjavaInterpreter> c = JinjavaInterpreter
231+
.closeablePushCurrent(interpreter)
232+
.get()
233+
) {
234+
List<String> list = List.of("foo");
235+
assertThatThrownBy(() ->
236+
jinjavaBeanELResolver.invoke(
237+
elContext,
238+
list.getClass().getMethod("get", int.class),
239+
"invoke",
240+
new Class[] { Object.class, Object[].class },
241+
new Object[] { list, 0 }
242+
)
243+
)
244+
.isInstanceOf(MethodNotFoundException.class);
245+
}
246+
}
189247
}

src/test/java/com/hubspot/jinjava/lib/tag/ForTagTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,15 @@ public void forLoopTupleWithNullValues() {
406406
assertThat(result).isEqualTo(" -1 -1 null null null ");
407407
}
408408

409+
@Test
410+
public void itUsesJinjavaRestrictedResolverOnReadingLoopVars() {
411+
String template =
412+
"""
413+
{% for _, config, class in ____int3rpr3t3r____ %}{{ class }}{% endfor %}""";
414+
String result = interpreter.render(template);
415+
assertThat(result).isEqualTo("");
416+
}
417+
409418
public static boolean inForLoop() {
410419
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();
411420
return interpreter.getContext().isInForLoop();

0 commit comments

Comments
 (0)