Skip to content

Commit

Permalink
Make restricted methods and properties to be configurable in `Jinjava…
Browse files Browse the repository at this point in the history
…Config` (#1076)

In `JinjavaBeanELResolver`, restrict methods and properties based on the
values from `JinjavaConfig` (`restrictedMethods`, `restrictedProperties`
and `deferredExecutionRestrictedMethods`.

---------

Co-authored-by: Manhey Chiu <mchiu@hubspot.com>
  • Loading branch information
manheychiu and manheychiu1 authored May 25, 2023
1 parent 33e0c60 commit bb95c0c
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 5 deletions.
31 changes: 30 additions & 1 deletion src/main/java/com/hubspot/jinjava/JinjavaConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.google.common.collect.ImmutableSet;
import com.hubspot.jinjava.el.JinjavaInterpreterResolver;
import com.hubspot.jinjava.el.JinjavaObjectUnwrapper;
import com.hubspot.jinjava.el.JinjavaProcessors;
Expand Down Expand Up @@ -63,7 +64,12 @@ public class JinjavaConfig {
private final boolean enableRecursiveMacroCalls;
private final int maxMacroRecursionDepth;

private final Map<Context.Library, Set<String>> disabled;
private final Map<Library, Set<String>> disabled;

private final Set<String> restrictedMethods;

private final Set<String> restrictedProperties;

private final boolean failOnUnknownTokens;
private final boolean nestedInterpretationEnabled;
private final RandomNumberGeneratorStrategy randomNumberGenerator;
Expand Down Expand Up @@ -120,6 +126,8 @@ private JinjavaConfig(Builder builder) {
timeZone = builder.timeZone;
maxRenderDepth = builder.maxRenderDepth;
disabled = builder.disabled;
restrictedMethods = builder.restrictedMethods;
restrictedProperties = builder.restrictedProperties;
trimBlocks = builder.trimBlocks;
lstripBlocks = builder.lstripBlocks;
enableRecursiveMacroCalls = builder.enableRecursiveMacroCalls;
Expand Down Expand Up @@ -217,6 +225,14 @@ public Map<Library, Set<String>> getDisabled() {
return disabled;
}

public Set<String> getRestrictedMethods() {
return restrictedMethods;
}

public Set<String> getRestrictedProperties() {
return restrictedProperties;
}

public boolean isFailOnUnknownTokens() {
return failOnUnknownTokens;
}
Expand Down Expand Up @@ -305,6 +321,9 @@ public static class Builder {
private long maxOutputSize = 0; // in bytes
private Map<Context.Library, Set<String>> disabled = new HashMap<>();

private Set<String> restrictedMethods = ImmutableSet.of();
private Set<String> restrictedProperties = ImmutableSet.of();

private boolean trimBlocks;
private boolean lstripBlocks;

Expand Down Expand Up @@ -355,6 +374,16 @@ public Builder withDisabled(Map<Context.Library, Set<String>> disabled) {
return this;
}

public Builder withRestrictedMethods(Set<String> restrictedMethods) {
this.restrictedMethods = ImmutableSet.copyOf(restrictedMethods);
return this;
}

public Builder withRestrictedProperties(Set<String> restrictedProperties) {
this.restrictedProperties = ImmutableSet.copyOf(restrictedProperties);
return this;
}

public Builder withMaxRenderDepth(int maxRenderDepth) {
this.maxRenderDepth = maxRenderDepth;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
* {@link BeanELResolver} supporting snake case property names.
*/
public class JinjavaBeanELResolver extends BeanELResolver {
private static final Set<String> RESTRICTED_PROPERTIES = ImmutableSet
private static final Set<String> DEFAULT_RESTRICTED_PROPERTIES = ImmutableSet
.<String>builder()
.add("class")
.build();

private static final Set<String> RESTRICTED_METHODS = ImmutableSet
private static final Set<String> DEFAULT_RESTRICTED_METHODS = ImmutableSet
.<String>builder()
.add("class")
.add("clone")
Expand Down Expand Up @@ -100,7 +100,16 @@ public Object invoke(
Class<?>[] paramTypes,
Object[] params
) {
if (method == null || RESTRICTED_METHODS.contains(method.toString())) {
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();

if (
method == null ||
DEFAULT_RESTRICTED_METHODS.contains(method.toString()) ||
(
interpreter != null &&
interpreter.getConfig().getRestrictedMethods().contains(method.toString())
)
) {
throw new MethodNotFoundException(
"Cannot find method '" + method + "' in " + base.getClass()
);
Expand Down Expand Up @@ -211,7 +220,15 @@ private static int pickMoreSpecificMethod(Method methodA, Method methodB) {
private String validatePropertyName(Object property) {
String propertyName = transformPropertyName(property);

if (RESTRICTED_PROPERTIES.contains(propertyName)) {
JinjavaInterpreter interpreter = JinjavaInterpreter.getCurrent();

if (
DEFAULT_RESTRICTED_PROPERTIES.contains(propertyName) ||
(
interpreter != null &&
interpreter.getConfig().getRestrictedProperties().contains(propertyName)
)
) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
package com.hubspot.jinjava.el.ext;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableSet;
import com.hubspot.jinjava.JinjavaConfig;
import com.hubspot.jinjava.el.JinjavaELContext;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import javax.el.ELContext;
import javax.el.MethodNotFoundException;
import javax.el.PropertyNotFoundException;
import org.junit.Before;
import org.junit.Test;

public class JinjavaBeanELResolverTest {
private JinjavaBeanELResolver jinjavaBeanELResolver;
private ELContext elContext;

JinjavaInterpreter interpreter = mock(JinjavaInterpreter.class);
JinjavaConfig config = mock(JinjavaConfig.class);

@Before
public void setUp() throws Exception {
jinjavaBeanELResolver = new JinjavaBeanELResolver();
elContext = new JinjavaELContext();
when(interpreter.getConfig()).thenReturn(config);
}

@Test
Expand Down Expand Up @@ -142,4 +154,29 @@ public String getResult(Number a, int b) {
)
.isEqualTo("int Integer"); // should be "Number int", but we can't figure that out
}

@Test
public void itThrowsExceptionWhenMethodIsRestrictedFromConfig() {
JinjavaInterpreter.pushCurrent(interpreter);
when(config.getRestrictedMethods()).thenReturn(ImmutableSet.of("foo"));
assertThatThrownBy(
() ->
jinjavaBeanELResolver.invoke(elContext, "abcd", "foo", null, new Object[] { 1 })
)
.isInstanceOf(MethodNotFoundException.class)
.hasMessageStartingWith("Cannot find method 'foo'");
JinjavaInterpreter.popCurrent();
}

@Test
public void itThrowsExceptionWhenPropertyIsRestrictedFromConfig() {
JinjavaInterpreter.pushCurrent(interpreter);
when(config.getRestrictedProperties()).thenReturn(ImmutableSet.of("property1"));
assertThatThrownBy(
() -> jinjavaBeanELResolver.getValue(elContext, "abcd", "property1")
)
.isInstanceOf(PropertyNotFoundException.class)
.hasMessageStartingWith("Could not find property");
JinjavaInterpreter.popCurrent();
}
}

0 comments on commit bb95c0c

Please sign in to comment.