Skip to content

Commit 1a8d43a

Browse files
committed
Add trimming note block to prevent accidentally creating expressions/tags/notes when PREVENT_ACCIDENTAL_EXPRESSIONS is enabled
1 parent 6fe39df commit 1a8d43a

2 files changed

Lines changed: 115 additions & 7 deletions

File tree

src/main/java/com/hubspot/jinjava/tree/output/OutputList.java

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.hubspot.jinjava.tree.output;
22

3-
import com.hubspot.jinjava.JinjavaConfig;
43
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
54
import com.hubspot.jinjava.interpret.OutputTooBigException;
65
import com.hubspot.jinjava.interpret.TemplateError;
6+
import com.hubspot.jinjava.tree.parse.TokenScannerSymbols;
77
import com.hubspot.jinjava.util.LengthLimitingStringBuilder;
88
import java.util.LinkedList;
99
import java.util.List;
@@ -49,18 +49,73 @@ public List<BlockPlaceholderOutputNode> getBlocks() {
4949
}
5050

5151
public String getValue() {
52-
boolean preventAccidentalExpressions = JinjavaInterpreter
52+
LengthLimitingStringBuilder val = new LengthLimitingStringBuilder(maxOutputSize);
53+
54+
return JinjavaInterpreter
5355
.getCurrentMaybe()
5456
.map(JinjavaInterpreter::getConfig)
55-
.map(JinjavaConfig::getFeatures)
57+
.filter(
58+
config ->
59+
config
60+
.getFeatures()
61+
.getActivationStrategy(PREVENT_ACCIDENTAL_EXPRESSIONS)
62+
.isActive(null)
63+
)
5664
.map(
57-
features ->
58-
features.getActivationStrategy(PREVENT_ACCIDENTAL_EXPRESSIONS).isActive(null)
65+
config -> joinNodesWithoutAddingExpressions(val, config.getTokenScannerSymbols())
5966
)
60-
.orElse(false);
67+
.orElseGet(() -> joinNodes(val));
68+
}
6169

62-
LengthLimitingStringBuilder val = new LengthLimitingStringBuilder(maxOutputSize);
70+
private String joinNodesWithoutAddingExpressions(
71+
LengthLimitingStringBuilder val,
72+
TokenScannerSymbols tokenScannerSymbols
73+
) {
74+
@SuppressWarnings("StringBufferReplaceableByString")
75+
String separator = new StringBuilder()
76+
.append('\n')
77+
.append(tokenScannerSymbols.getPrefixChar())
78+
.append(tokenScannerSymbols.getNoteChar())
79+
.append(tokenScannerSymbols.getTrimChar())
80+
.append(' ')
81+
.append(tokenScannerSymbols.getNoteChar())
82+
.append(tokenScannerSymbols.getExprEndChar())
83+
.toString();
84+
String prev = null;
85+
String cur;
86+
for (OutputNode node : nodes) {
87+
try {
88+
cur = node.getValue();
89+
if (
90+
prev != null &&
91+
prev.length() > 0 &&
92+
prev.charAt(prev.length() - 1) == tokenScannerSymbols.getExprStartChar()
93+
) {
94+
if (
95+
cur.length() > 0 &&
96+
(
97+
cur.charAt(0) == tokenScannerSymbols.getTag() ||
98+
cur.charAt(0) == tokenScannerSymbols.getExprStartChar() ||
99+
cur.charAt(0) == tokenScannerSymbols.getNoteChar()
100+
)
101+
) {
102+
val.append(separator);
103+
}
104+
}
105+
prev = cur;
106+
val.append(node.getValue());
107+
} catch (OutputTooBigException e) {
108+
JinjavaInterpreter
109+
.getCurrent()
110+
.addError(TemplateError.fromOutputTooBigException(e));
111+
return val.toString();
112+
}
113+
}
114+
115+
return val.toString();
116+
}
63117

118+
private String joinNodes(LengthLimitingStringBuilder val) {
64119
for (OutputNode node : nodes) {
65120
try {
66121
val.append(node.getValue());

src/test/java/com/hubspot/jinjava/interpret/JinjavaInterpreterTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@
88
import com.google.common.collect.Lists;
99
import com.hubspot.jinjava.Jinjava;
1010
import com.hubspot.jinjava.JinjavaConfig;
11+
import com.hubspot.jinjava.features.FeatureConfig;
12+
import com.hubspot.jinjava.features.FeatureStrategies;
1113
import com.hubspot.jinjava.interpret.JinjavaInterpreter.InterpreterScopeClosable;
1214
import com.hubspot.jinjava.interpret.TemplateError.ErrorItem;
1315
import com.hubspot.jinjava.interpret.TemplateError.ErrorReason;
1416
import com.hubspot.jinjava.interpret.TemplateError.ErrorType;
17+
import com.hubspot.jinjava.mode.EagerExecutionMode;
1518
import com.hubspot.jinjava.mode.PreserveRawExecutionMode;
1619
import com.hubspot.jinjava.objects.date.FormattedDate;
1720
import com.hubspot.jinjava.objects.date.StrftimeFormatter;
1821
import com.hubspot.jinjava.tree.TextNode;
1922
import com.hubspot.jinjava.tree.output.BlockInfo;
23+
import com.hubspot.jinjava.tree.output.OutputList;
2024
import com.hubspot.jinjava.tree.parse.TextToken;
2125
import com.hubspot.jinjava.tree.parse.TokenScannerSymbols;
2226
import java.time.ZoneId;
@@ -503,4 +507,53 @@ public void itFiltersDuplicateErrors() {
503507

504508
assertThat(interpreter.getErrors()).containsExactly(error1, error2);
505509
}
510+
511+
@Test
512+
public void itPreventsAccidentalExpressions() {
513+
String makeExpression = "if (true) {\n{%- print deferred -%}\n}";
514+
String makeTag = "if (true) {\n{%- print '% print 123 %' -%}\n}";
515+
String makeNote = "if (true) {\n{%- print '# note #' -%}\n}";
516+
jinjava.getGlobalContext().put("deferred", DeferredValue.instance());
517+
518+
JinjavaInterpreter normalInterpreter = new JinjavaInterpreter(
519+
jinjava,
520+
jinjava.getGlobalContext(),
521+
JinjavaConfig.newBuilder().withExecutionMode(EagerExecutionMode.instance()).build()
522+
);
523+
JinjavaInterpreter preventingInterpreter = new JinjavaInterpreter(
524+
jinjava,
525+
jinjava.getGlobalContext(),
526+
JinjavaConfig
527+
.newBuilder()
528+
.withFeatureConfig(
529+
FeatureConfig
530+
.newBuilder()
531+
.add(OutputList.PREVENT_ACCIDENTAL_EXPRESSIONS, FeatureStrategies.ACTIVE)
532+
.build()
533+
)
534+
.withExecutionMode(EagerExecutionMode.instance())
535+
.build()
536+
);
537+
JinjavaInterpreter.pushCurrent(normalInterpreter);
538+
try {
539+
assertThat(normalInterpreter.render(makeExpression))
540+
.isEqualTo("if (true) {{% print deferred %}}");
541+
assertThat(normalInterpreter.render(makeTag))
542+
.isEqualTo("if (true) {% print 123 %}");
543+
assertThat(normalInterpreter.render(makeNote)).isEqualTo("if (true) {# note #}");
544+
} finally {
545+
JinjavaInterpreter.popCurrent();
546+
}
547+
JinjavaInterpreter.pushCurrent(preventingInterpreter);
548+
try {
549+
assertThat(preventingInterpreter.render(makeExpression))
550+
.isEqualTo("if (true) {\n" + "{#- #}{% print deferred %}}");
551+
assertThat(preventingInterpreter.render(makeTag))
552+
.isEqualTo("if (true) {\n" + "{#- #}% print 123 %}");
553+
assertThat(preventingInterpreter.render(makeNote))
554+
.isEqualTo("if (true) {\n" + "{#- #}# note #}");
555+
} finally {
556+
JinjavaInterpreter.popCurrent();
557+
}
558+
}
506559
}

0 commit comments

Comments
 (0)