From 4f61e0ee4482e60e3ca88a747153d412f864bc2d Mon Sep 17 00:00:00 2001 From: j-sandy <30489233+j-sandy@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:00:48 +0530 Subject: [PATCH] feat(SpEL): implement to configure the limit of characters for SpEL expressions Spring Expression Lanuage (SpEL) has a default limit of 10,000 characters. Springframework provides the feature to configure the limit. This feature allows to configure the limit of characters for SpEL expressions. Approach: In order to use an expression with characters more than the given default limit, require to follow either of the below approaches: 1. For Springframework >=5.3.28 and <6.1.3, by setting `maximumExpressionLength` field while instantiating the custom `SpelParserConfiguration` class. https://github.com/spring-projects/spring-framework/issues/30380 https://github.com/spring-projects/spring-framework/issues/30446 2. For Springframework >=6.1.3, by setting a JVM system property or Spring property named `spring.context.expression.maxLength` to the maximum expression length needed by your application. https://github.com/spring-projects/spring-framework/issues/31952 https://github.com/spring-projects/spring-framework/commit/785598629abda944343a02307ad82a79bb31b589 Spinnaker supports spring boot 2.7.18, that brings springframework 5.3.31 [https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/dependency-versions.html#appendix.dependency-versions.propertie9]. So first approach need to be implemented along with spinnaker enhancement to expose the `maximumExpressionLength` field. --- .../PipelineExpressionEvaluator.java | 10 ++- .../PipelineExpressionLengthTest.java | 72 +++++++++++++++++++ .../test/resources/expression-properties.yml | 2 + 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionLengthTest.java create mode 100644 orca-core/src/test/resources/expression-properties.yml diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionEvaluator.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionEvaluator.java index 0f60c92f43..e88379e5a2 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionEvaluator.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionEvaluator.java @@ -35,6 +35,7 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParserContext; import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.SpelParserConfiguration; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -130,8 +131,7 @@ public static SpelEvaluatorVersion Default() { PipelineExecution.AuthenticationDetails.class, PipelineExecution.PausedDetails.class }; - - private final ExpressionParser parser = new SpelExpressionParser(); + private final ExpressionParser parser; private final ParserContext parserContext = new TemplateParserContext("${", "}"); private final ExpressionsSupport support; @@ -148,6 +148,12 @@ public PipelineExpressionEvaluator( pluginManager, expressionProperties); initExecutionAwareFunctions(expressionFunctionProviders); + parser = + new SpelExpressionParser( + expressionProperties.getMaxExpressionLength() > 0 + ? new SpelParserConfiguration( + null, null, false, false, 0, expressionProperties.getMaxExpressionLength()) + : new SpelParserConfiguration()); } public Map evaluate( diff --git a/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionLengthTest.java b/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionLengthTest.java new file mode 100644 index 0000000000..4b35d84443 --- /dev/null +++ b/orca-core/src/test/java/com/netflix/spinnaker/orca/pipeline/expressions/PipelineExpressionLengthTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2024 OpsMx, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.netflix.spinnaker.orca.pipeline.expressions; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.netflix.spinnaker.kork.expressions.ExpressionEvaluationSummary; +import com.netflix.spinnaker.kork.expressions.config.ExpressionProperties; +import com.netflix.spinnaker.orca.test.YamlFileApplicationContextInitializer; +import java.util.ArrayList; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.pf4j.PluginManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration( + classes = PipelineExpressionLengthTest.class, + initializers = PipelineExpressionLengthTest.class) +@SpringBootTest +@EnableConfigurationProperties(ExpressionProperties.class) +public class PipelineExpressionLengthTest extends YamlFileApplicationContextInitializer { + + @Mock private PluginManager pluginManager; + @Autowired private ExpressionProperties expressionProperties; + + @Override + protected String getResourceLocation() { + return "classpath:expression-properties.yml"; + } + + @Test + void customExpressionLength() { + String expression = String.format("%s", repeat("T", 10975)); + String rootObjectExpression = String.format("${status.toString() == \"%s\"}", expression); + + Map rootObject = Map.of("status", expression); + Map source = Map.of("test", rootObjectExpression); + + PipelineExpressionEvaluator evaluator = + new PipelineExpressionEvaluator(new ArrayList<>(), pluginManager, expressionProperties); + + Map result = + evaluator.evaluate(source, rootObject, new ExpressionEvaluationSummary(), true); + assertTrue(Boolean.parseBoolean(result.get("test").toString())); + } + + private String repeat(String str, int count) { + String res = ""; + for (int i = 0; i < count; i++) { + res += str; + } + return res; + } +} diff --git a/orca-core/src/test/resources/expression-properties.yml b/orca-core/src/test/resources/expression-properties.yml new file mode 100644 index 0000000000..aecfde7004 --- /dev/null +++ b/orca-core/src/test/resources/expression-properties.yml @@ -0,0 +1,2 @@ +expression: + max-expression-length: 11000