Skip to content

Commit f94f35c

Browse files
committed
Don't expect ____int3rpr3t3r____ and don't use arrays in ReverseFilter
and add method and return type validator to test classes
1 parent ab086b3 commit f94f35c

90 files changed

Lines changed: 715 additions & 312 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ String[] allowedDeclaredMethodsFromClasses() {
100100
AbstractCollection.class.getCanonicalName(),
101101
LinkedHashMap.class.getCanonicalName(),
102102
"%s.Entry".formatted(LinkedHashMap.class.getCanonicalName()),
103+
"%s.LinkedValues".formatted(LinkedHashMap.class.getCanonicalName()),
103104
};
104105

105106
@Override
@@ -146,6 +147,11 @@ String[] allowedReturnTypeCanonicalClassPrefixes() {
146147
String[] allowedDeclaredMethodsFromCanonicalClassPrefixes() {
147148
return ARRAY;
148149
}
150+
151+
@Override
152+
String[] allowedReturnTypeCanonicalClassPrefixes() {
153+
return ARRAY;
154+
}
149155
};
150156

151157
Method[] allowMethods() {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public Object validateReturnType(Object o) {
3939
return null;
4040
}
4141
Class<?> clazz = o.getClass();
42+
while (clazz.isArray()) {
43+
clazz = clazz.getComponentType();
44+
}
4245
String canonicalClassName = clazz.getCanonicalName();
4346
boolean isAllowedClassName = allowedReturnTypesCache.computeIfAbsent(
4447
canonicalClassName,

src/main/java/com/hubspot/jinjava/lib/filter/RenderFilter.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.hubspot.jinjava.lib.filter;
22

3-
import com.hubspot.jinjava.JinjavaConfig;
43
import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
54
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
65
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
@@ -30,10 +29,7 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args)
3029
String firstArg = args[0];
3130
return interpreter.renderFlat(
3231
Objects.toString(var),
33-
NumberUtils.toLong(
34-
firstArg,
35-
JinjavaConfig.newBuilder().build().getMaxOutputSize()
36-
)
32+
NumberUtils.toLong(firstArg, interpreter.getConfig().getMaxOutputSize())
3733
);
3834
}
3935
return interpreter.renderFlat(Objects.toString(var));

src/main/java/com/hubspot/jinjava/lib/filter/ReverseFilter.java

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
2020
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
2121
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
22-
import java.lang.reflect.Array;
2322
import java.util.Collection;
23+
import java.util.Iterator;
24+
import java.util.NoSuchElementException;
2425

2526
@JinjavaDoc(
2627
value = "Reverse the object or return an iterator the iterates over it the other way round.",
@@ -48,24 +49,11 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar
4849
}
4950
// collection
5051
if (object instanceof Collection) {
51-
Object[] origin = ((Collection<?>) object).toArray();
52-
int length = origin.length;
53-
Object[] res = new Object[length];
54-
length--;
55-
for (int i = 0; i <= length; i++) {
56-
res[i] = origin[length - i];
57-
}
58-
return res;
52+
return ReverseArrayIterator.create(((Collection<?>) object).toArray());
5953
}
6054
// array
6155
if (object.getClass().isArray()) {
62-
int length = Array.getLength(object);
63-
Object[] res = new Object[length];
64-
length--;
65-
for (int i = 0; i <= length; i++) {
66-
res[i] = Array.get(object, length - i);
67-
}
68-
return res;
56+
return ReverseArrayIterator.create((Object[]) object);
6957
}
7058
// string
7159
if (object instanceof String) {
@@ -86,4 +74,32 @@ public Object filter(Object object, JinjavaInterpreter interpreter, String... ar
8674
public String getName() {
8775
return "reverse";
8876
}
77+
78+
static class ReverseArrayIterator<T> implements Iterator<T> {
79+
80+
private final T[] array;
81+
private int index;
82+
83+
static <T> ReverseArrayIterator<T> create(T[] array) {
84+
return new ReverseArrayIterator<>(array);
85+
}
86+
87+
private ReverseArrayIterator(T[] array) {
88+
this.array = array;
89+
index = array.length - 1;
90+
}
91+
92+
@Override
93+
public T next() {
94+
if (index < 0) {
95+
throw new NoSuchElementException();
96+
}
97+
return array[index--];
98+
}
99+
100+
@Override
101+
public boolean hasNext() {
102+
return index >= 0;
103+
}
104+
}
89105
}

src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerCycleTag.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.google.common.annotations.Beta;
44
import com.google.common.collect.ImmutableMap;
5-
import com.hubspot.jinjava.el.ext.ExtendedParser;
65
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
76
import com.hubspot.jinjava.interpret.TemplateSyntaxException;
87
import com.hubspot.jinjava.lib.tag.CycleTag;
@@ -239,19 +238,17 @@ private static String getIsIterable(String var, int forIndex, TagToken tagToken)
239238
String tokenEnd = tagToken.getSymbols().getExpressionEndWithTag();
240239
return (
241240
String.format(
242-
"%s if exptest:iterable.evaluate(%s, %s) %s",
241+
"%s if exptest:iterable.evaluate(%s, null) %s",
243242
tokenStart,
244243
var,
245-
ExtendedParser.INTERPRETER,
246244
tokenEnd
247245
) +
248246
// modulo indexing
249247
String.format(
250-
"{{ %s[%d %% filter:length.filter(%s, %s)] }}",
248+
"{{ %s[%d %% filter:length.filter(%s, null)] }}",
251249
var,
252250
forIndex,
253-
var,
254-
ExtendedParser.INTERPRETER
251+
var
255252
) +
256253
String.format("%s else %s", tokenStart, tokenEnd) +
257254
String.format("{{ %s }}", var) +
Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,42 @@
11
package com.hubspot.jinjava;
22

3+
import com.hubspot.jinjava.el.ext.AllowlistMethodValidator;
4+
import com.hubspot.jinjava.el.ext.AllowlistReturnTypeValidator;
5+
import com.hubspot.jinjava.el.ext.MethodValidatorConfig;
6+
import com.hubspot.jinjava.el.ext.ReturnTypeValidatorConfig;
37
import org.junit.Before;
48

59
public abstract class BaseJinjavaTest {
610

11+
public static final AllowlistMethodValidator METHOD_VALIDATOR =
12+
AllowlistMethodValidator.create(
13+
MethodValidatorConfig
14+
.builder()
15+
.addDefaultAllowlistGroups()
16+
.addAllowedDeclaredMethodsFromCanonicalClassPrefixes(
17+
"com.hubspot.jinjava.testobjects"
18+
)
19+
.build()
20+
);
21+
public static final AllowlistReturnTypeValidator RETURN_TYPE_VALIDATOR =
22+
AllowlistReturnTypeValidator.create(
23+
ReturnTypeValidatorConfig
24+
.builder()
25+
.addDefaultAllowlistGroups()
26+
.addAllowedCanonicalClassPrefixes("com.hubspot.jinjava.testobjects")
27+
.build()
28+
);
729
public Jinjava jinjava;
830

931
@Before
1032
public void baseSetup() {
11-
jinjava = new Jinjava();
33+
jinjava =
34+
new Jinjava(
35+
JinjavaConfig
36+
.newBuilder()
37+
.withMethodValidator(METHOD_VALIDATOR)
38+
.withReturnTypeValidator(RETURN_TYPE_VALIDATOR)
39+
.build()
40+
);
1241
}
1342
}

src/test/java/com/hubspot/jinjava/EagerTest.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public Optional<LocationResolver> getLocationResolver() {
7474
);
7575
JinjavaConfig config = JinjavaConfig
7676
.newBuilder()
77+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
78+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
7779
.withRandomNumberGeneratorStrategy(RandomNumberGeneratorStrategy.DEFERRED)
7880
.withExecutionMode(executionMode)
7981
.withNestedInterpretationEnabled(true)
@@ -216,7 +218,7 @@ public void itPreserveDeferredVariableResolvingEqualToInOrCondition() {
216218

217219
assertThat(output)
218220
.isEqualTo(
219-
"{% if false || exptest:equalto.evaluate('a', ____int3rpr3t3r____, deferred) %}preserved{% endif %}"
221+
"{% if false || exptest:equalto.evaluate('a', null, deferred) %}preserved{% endif %}"
220222
);
221223
assertThat(interpreter.getErrors()).isEmpty();
222224
localContext.put("deferred", "a");
@@ -307,8 +309,7 @@ public void itPreservesForTag() {
307309
@Test
308310
public void itPreservesFilters() {
309311
String output = interpreter.render("{{ deferred|capitalize }}");
310-
assertThat(output)
311-
.isEqualTo("{{ filter:capitalize.filter(deferred, ____int3rpr3t3r____) }}");
312+
assertThat(output).isEqualTo("{{ filter:capitalize.filter(deferred, null) }}");
312313
assertThat(interpreter.getErrors()).isEmpty();
313314
localContext.put("deferred", "foo");
314315
assertThat(interpreter.render(output)).isEqualTo("Foo");
@@ -318,17 +319,14 @@ public void itPreservesFilters() {
318319
public void itPreservesFunctions() {
319320
String output = interpreter.render("{{ deferred|datetimeformat('%B %e, %Y') }}");
320321
assertThat(output)
321-
.isEqualTo(
322-
"{{ filter:datetimeformat.filter(deferred, ____int3rpr3t3r____, '%B %e, %Y') }}"
323-
);
322+
.isEqualTo("{{ filter:datetimeformat.filter(deferred, null, '%B %e, %Y') }}");
324323
assertThat(interpreter.getErrors()).isEmpty();
325324
}
326325

327326
@Test
328327
public void itPreservesRandomness() {
329328
String output = interpreter.render("{{ [1, 2, 3]|shuffle }}");
330-
assertThat(output)
331-
.isEqualTo("{{ filter:shuffle.filter([1, 2, 3], ____int3rpr3t3r____) }}");
329+
assertThat(output).isEqualTo("{{ filter:shuffle.filter([1, 2, 3], null) }}");
332330
assertThat(interpreter.getErrors()).isEmpty();
333331
}
334332

@@ -779,6 +777,8 @@ public void itHandlesAutoEscape() {
779777
public void itWrapsCertainOutputInRaw() {
780778
JinjavaConfig config = JinjavaConfig
781779
.newBuilder()
780+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
781+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
782782
.withRandomNumberGeneratorStrategy(RandomNumberGeneratorStrategy.DEFERRED)
783783
.withExecutionMode(EagerExecutionMode.instance())
784784
.withNestedInterpretationEnabled(false)
@@ -865,13 +865,20 @@ public void itHandlesUnknownFunctionErrors() {
865865
JinjavaInterpreter eagerInterpreter = new JinjavaInterpreter(
866866
jinjava,
867867
jinjava.getGlobalContextCopy(),
868-
JinjavaConfig.newBuilder().withExecutionMode(EagerExecutionMode.instance()).build()
868+
JinjavaConfig
869+
.newBuilder()
870+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
871+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
872+
.withExecutionMode(EagerExecutionMode.instance())
873+
.build()
869874
);
870875
JinjavaInterpreter defaultInterpreter = new JinjavaInterpreter(
871876
jinjava,
872877
jinjava.getGlobalContextCopy(),
873878
JinjavaConfig
874879
.newBuilder()
880+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
881+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
875882
.withExecutionMode(DefaultExecutionMode.instance())
876883
.build()
877884
);

src/test/java/com/hubspot/jinjava/ExpectedTemplateInterpreter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public String assertExpectedNonEagerOutput(String name) {
7878
jinjava.getGlobalContextCopy(),
7979
JinjavaConfig
8080
.newBuilder()
81+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
82+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
8183
.withExecutionMode(DefaultExecutionMode.instance())
8284
.withNestedInterpretationEnabled(true)
8385
.withLegacyOverrides(
@@ -109,6 +111,8 @@ public String assertExpectedNonEagerOutput(String name) {
109111
jinjava.getGlobalContextCopy(),
110112
JinjavaConfig
111113
.newBuilder()
114+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
115+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
112116
.withExecutionMode(DefaultExecutionMode.instance())
113117
.withNestedInterpretationEnabled(true)
114118
.withLegacyOverrides(

src/test/java/com/hubspot/jinjava/FullSnippetsTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public Optional<LocationResolver> getLocationResolver() {
5252
);
5353
JinjavaConfig config = JinjavaConfig
5454
.newBuilder()
55+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
56+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
5557
.withNestedInterpretationEnabled(true)
5658
.withLegacyOverrides(
5759
LegacyOverrides.newBuilder().withUsePyishObjectMapper(true).build()

src/test/java/com/hubspot/jinjava/el/ExpressionResolverTest.java

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.google.common.collect.ImmutableSet;
99
import com.google.common.collect.Lists;
1010
import com.google.common.collect.Maps;
11+
import com.hubspot.jinjava.BaseJinjavaTest;
1112
import com.hubspot.jinjava.Jinjava;
1213
import com.hubspot.jinjava.JinjavaConfig;
1314
import com.hubspot.jinjava.interpret.Context;
@@ -481,6 +482,8 @@ public void itBlocksDisabledFunctions() {
481482

482483
final JinjavaConfig config = JinjavaConfig
483484
.newBuilder()
485+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
486+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
484487
.withDisabled(disabled)
485488
.build();
486489

@@ -514,7 +517,11 @@ public void itBlocksDisabledExpTests() {
514517
@Test
515518
public void itStoresResolvedFunctions() {
516519
context.put("datetime", 12345);
517-
final JinjavaConfig config = JinjavaConfig.newBuilder().build();
520+
final JinjavaConfig config = JinjavaConfig
521+
.newBuilder()
522+
.withMethodValidator(BaseJinjavaTest.METHOD_VALIDATOR)
523+
.withReturnTypeValidator(BaseJinjavaTest.RETURN_TYPE_VALIDATOR)
524+
.build();
518525
String template =
519526
"{% for i in range(1, 5) %}{{i}} {% endfor %}\n{{ unixtimestamp(datetime) }}";
520527
final RenderResult renderResult = jinjava.renderForResult(template, context, config);
@@ -618,46 +625,36 @@ public void itResolvesAlternateExpTestSyntax() {
618625
assertThat(interpreter.render("{% if 2 is even %}yes{% endif %}")).isEqualTo("yes");
619626

620627
assertThat(
621-
interpreter.render(
622-
"{% if exptest:even.evaluate(2, ____int3rpr3t3r____) %}yes{% endif %}"
623-
)
628+
interpreter.render("{% if exptest:even.evaluate(2, null) %}yes{% endif %}")
624629
)
625630
.isEqualTo("yes");
626631
assertThat(
627-
interpreter.render(
628-
"{% if exptest:false.evaluate(false, ____int3rpr3t3r____) %}yes{% endif %}"
629-
)
632+
interpreter.render("{% if exptest:false.evaluate(false, null) %}yes{% endif %}")
630633
)
631634
.isEqualTo("yes");
632635
}
633636

634637
@Test
635638
public void itResolvesAlternateExpTestSyntaxForTrueAndFalseExpTests() {
636639
assertThat(
637-
interpreter.render(
638-
"{% if exptest:false.evaluate(false, ____int3rpr3t3r____) %}yes{% endif %}"
639-
)
640+
interpreter.render("{% if exptest:false.evaluate(false, null) %}yes{% endif %}")
640641
)
641642
.isEqualTo("yes");
642643
assertThat(
643-
interpreter.render(
644-
"{% if exptest:true.evaluate(true, ____int3rpr3t3r____) %}yes{% endif %}"
645-
)
644+
interpreter.render("{% if exptest:true.evaluate(true, null) %}yes{% endif %}")
646645
)
647646
.isEqualTo("yes");
648647
}
649648

650649
@Test
651650
public void itResolvesAlternateExpTestSyntaxForInExpTests() {
652651
assertThat(
653-
interpreter.render(
654-
"{% if exptest:in.evaluate(1, ____int3rpr3t3r____, [1]) %}yes{% endif %}"
655-
)
652+
interpreter.render("{% if exptest:in.evaluate(1, null, [1]) %}yes{% endif %}")
656653
)
657654
.isEqualTo("yes");
658655
assertThat(
659656
interpreter.render(
660-
"{% if exptest:in.evaluate(2, ____int3rpr3t3r____, [1]) %}yes{% else %}no{% endif %}"
657+
"{% if exptest:in.evaluate(2, null, [1]) %}yes{% else %}no{% endif %}"
661658
)
662659
)
663660
.isEqualTo("no");

0 commit comments

Comments
 (0)