From c76b7753709ef2b70229d72b1adfd2dbcae08c7e Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 14 Feb 2022 18:42:21 -0500 Subject: [PATCH] Adds spring.cloud.gateway.restrictive-property-accessor.enabled To disable the restrictive accessor, set spring.cloud.gateway.restrictive-property-accessor.enabled=false. --- .../gateway/support/ShortcutConfigurable.java | 29 +++++- ...itional-spring-configuration-metadata.json | 6 ++ .../support/ShortcutConfigurableTests.java | 92 +++++++++++++++++++ 3 files changed, 123 insertions(+), 4 deletions(-) diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java index 22a2d1ea..a620e48b 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/ShortcutConfigurable.java @@ -25,6 +25,7 @@ import java.util.stream.Collectors; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.core.env.Environment; import org.springframework.expression.BeanResolver; import org.springframework.expression.ConstructorResolver; import org.springframework.expression.EvaluationContext; @@ -38,6 +39,7 @@ import org.springframework.expression.TypeLocator; import org.springframework.expression.TypedValue; import org.springframework.expression.common.TemplateParserContext; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.ReflectivePropertyAccessor; import org.springframework.expression.spel.support.SimpleEvaluationContext; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -65,7 +67,7 @@ public interface ShortcutConfigurable { } if (rawValue != null && rawValue.startsWith("#{") && entryValue.endsWith("}")) { // assume it's spel - GatewayEvaluationContext context = new GatewayEvaluationContext(new BeanFactoryResolver(beanFactory)); + GatewayEvaluationContext context = new GatewayEvaluationContext(beanFactory); Expression expression = parser.parseExpression(entryValue, new TemplateParserContext()); value = expression.getValue(context); } @@ -162,10 +164,20 @@ public interface ShortcutConfigurable { private final BeanFactoryResolver beanFactoryResolver; - private SimpleEvaluationContext delegate = SimpleEvaluationContext.forReadOnlyDataBinding().build(); + private final SimpleEvaluationContext delegate; - public GatewayEvaluationContext(BeanFactoryResolver beanFactoryResolver) { - this.beanFactoryResolver = beanFactoryResolver; + public GatewayEvaluationContext(BeanFactory beanFactory) { + this.beanFactoryResolver = new BeanFactoryResolver(beanFactory); + Environment env = beanFactory.getBean(Environment.class); + boolean restrictive = env.getProperty("spring.cloud.gateway.restrictive-property-accessor.enabled", + Boolean.class, true); + if (restrictive) { + delegate = SimpleEvaluationContext.forPropertyAccessors(new RestrictivePropertyAccessor()) + .withMethodResolvers((context, targetObject, name, argumentTypes) -> null).build(); + } + else { + delegate = SimpleEvaluationContext.forReadOnlyDataBinding().build(); + } } @Override @@ -227,4 +239,13 @@ public interface ShortcutConfigurable { } + class RestrictivePropertyAccessor extends ReflectivePropertyAccessor { + + @Override + public boolean canRead(EvaluationContext context, Object target, String name) { + return false; + } + + } + } diff --git a/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 0e159b86..42e76321 100644 --- a/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-gateway-server/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -365,6 +365,12 @@ "type": "java.lang.Integer", "description": "The order of RoutePredicateHandlerMapping.", "defaultValue": "1" + }, + { + "name": "spring.cloud.gateway.restrictive-property-accessor.enabled", + "type": "java.lang.Boolean", + "description": "Restricts method and property access in SpEL.", + "defaultValue": "true" } ] } diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java index 158959bc..42f7ef19 100644 --- a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/support/ShortcutConfigurableTests.java @@ -28,8 +28,10 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.cloud.gateway.support.ShortcutConfigurable.ShortcutType; import org.springframework.context.annotation.Bean; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.test.context.junit4.SpringRunner; @@ -44,6 +46,9 @@ public class ShortcutConfigurableTests { @Autowired BeanFactory beanFactory; + @Autowired + ConfigurableEnvironment env; + private SpelExpressionParser parser; @Test @@ -63,6 +68,42 @@ public class ShortcutConfigurableTests { }).isInstanceOf(SpelEvaluationException.class); } + @Test + public void testNormalizeDefaultTypeWithSpelAndInvalidPropertyReferenceFails() { + TestPropertyValues.of("spring.cloud.gateway.restrictive-property-accessor.enabled=true").applyTo(env); + parser = new SpelExpressionParser(); + ShortcutConfigurable shortcutConfigurable = new ShortcutConfigurable() { + @Override + public List shortcutFieldOrder() { + return Arrays.asList("bean", "arg1"); + } + }; + Map args = new HashMap<>(); + args.put("barproperty", "#{@bar.getInt}"); + args.put("arg1", "val1"); + assertThatThrownBy(() -> { + ShortcutType.DEFAULT.normalize(args, shortcutConfigurable, parser, this.beanFactory); + }).isInstanceOf(SpelEvaluationException.class); + } + + @Test + public void testNormalizeDefaultTypeWithSpelAndInvalidMethodReferenceFails() { + TestPropertyValues.of("ispring.cloud.gateway.restrictve-property-accessor.enabled=true").applyTo(env); + parser = new SpelExpressionParser(); + ShortcutConfigurable shortcutConfigurable = new ShortcutConfigurable() { + @Override + public List shortcutFieldOrder() { + return Arrays.asList("bean", "arg1"); + } + }; + Map args = new HashMap<>(); + args.put("barmethod", "#{@bar.myMethod}"); + args.put("arg1", "val1"); + assertThatThrownBy(() -> { + ShortcutType.DEFAULT.normalize(args, shortcutConfigurable, parser, this.beanFactory); + }).isInstanceOf(SpelEvaluationException.class); + } + @Test public void testNormalizeDefaultTypeWithSpel() { parser = new SpelExpressionParser(); @@ -79,6 +120,40 @@ public class ShortcutConfigurableTests { assertThat(map).isNotNull().containsEntry("bean", 42).containsEntry("arg1", "val1"); } + @Test + public void testNormalizeDefaultTypeWithSpelAndPropertyReferenceEnabled() { + TestPropertyValues.of("spring.cloud.gateway.restrictive-property-accessor.enabled=false").applyTo(env); + parser = new SpelExpressionParser(); + ShortcutConfigurable shortcutConfigurable = new ShortcutConfigurable() { + @Override + public List shortcutFieldOrder() { + return Arrays.asList("bean", "arg1"); + } + }; + Map args = new HashMap<>(); + args.put("barproperty", "#{@bar.getInt}"); + args.put("arg1", "val1"); + Map map = ShortcutType.DEFAULT.normalize(args, shortcutConfigurable, parser, this.beanFactory); + assertThat(map).isNotNull().containsEntry("barproperty", 42).containsEntry("arg1", "val1"); + } + + @Test + public void testNormalizeDefaultTypeWithSpelAndMethodReferenceEnabled() { + TestPropertyValues.of("spring.cloud.gateway.restrictive-property-accessor.enabled=false").applyTo(env); + parser = new SpelExpressionParser(); + ShortcutConfigurable shortcutConfigurable = new ShortcutConfigurable() { + @Override + public List shortcutFieldOrder() { + return Arrays.asList("bean", "arg1"); + } + }; + Map args = new HashMap<>(); + args.put("barmethod", "#{@bar.myMethod}"); + args.put("arg1", "val1"); + Map map = ShortcutType.DEFAULT.normalize(args, shortcutConfigurable, parser, this.beanFactory); + assertThat(map).isNotNull().containsEntry("barmethod", 42).containsEntry("arg1", "val1"); + } + @Test @SuppressWarnings("unchecked") public void testNormalizeGatherListTypeWithSpel() { @@ -155,6 +230,23 @@ public class ShortcutConfigurableTests { return 42; } + @Bean + public Bar bar() { + return new Bar(); + } + + } + + protected static class Bar { + + public int getInt() { + return 42; + } + + public int myMethod() { + return 42; + } + } }