This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new 124bf3da3cfb [backport camel-4.18.x] CAMEL-24901: camel-aws2-s3 -
restrict Simple expression evaluation of key/bucket to configured values
(#26842)
124bf3da3cfb is described below
commit 124bf3da3cfb5d8e1994471e1301b6421bfe96d2
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Sep 25 11:23:58 2026 +0200
[backport camel-4.18.x] CAMEL-24901: camel-aws2-s3 - restrict Simple
expression evaluation of key/bucket to configured values (#26842)
Signed-off-by: Andrea Cosentino <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
---
.../camel/component/aws2/s3/utils/AWS2S3Utils.java | 26 +++--
.../S3GetObjectDynamicKeyOperationIT.java | 6 +-
.../component/aws2/s3/utils/AWS2S3UtilsTest.java | 129 +++++++++++++++++++++
3 files changed, 146 insertions(+), 15 deletions(-)
diff --git
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/utils/AWS2S3Utils.java
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/utils/AWS2S3Utils.java
index 37c008ca8f9a..76e403eda855 100644
---
a/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/utils/AWS2S3Utils.java
+++
b/components/camel-aws/camel-aws2-s3/src/main/java/org/apache/camel/component/aws2/s3/utils/AWS2S3Utils.java
@@ -50,15 +50,16 @@ public final class AWS2S3Utils {
public static String determineBucketName(final Exchange exchange,
AWS2S3Configuration configuration) {
String bucketName =
exchange.getIn().getHeader(AWS2S3Constants.OVERRIDE_BUCKET_NAME, String.class);
if (ObjectHelper.isEmpty(bucketName)) {
+ // only the configured bucket name (supplied by the route) may be
a dynamic simple expression;
+ // a bucket name provided through the header is used literally and
never evaluated
bucketName = configuration.getBucketName();
+ if (bucketName != null && hasSimpleFunction(bucketName)) {
+ Language simple =
exchange.getContext().resolveLanguage("simple");
+ bucketName =
simple.createExpression(bucketName).evaluate(exchange, String.class);
+ }
}
if (bucketName == null) {
- throw new IllegalArgumentException("AWS S3 Bucket name header is
missing or not configured.");
- }
- // dynamic keys using built-in simple language
- if (hasSimpleFunction(bucketName)) {
- Language simple = exchange.getContext().resolveLanguage("simple");
- bucketName =
simple.createExpression(bucketName).evaluate(exchange, String.class);
+ throw new IllegalArgumentException("AWS S3 Bucket name is not set,
or resolved to null.");
}
return bucketName;
}
@@ -137,15 +138,16 @@ public final class AWS2S3Utils {
public static String determineKey(final Exchange exchange,
AWS2S3Configuration configuration) {
String key = exchange.getIn().getHeader(AWS2S3Constants.KEY,
String.class);
if (ObjectHelper.isEmpty(key)) {
+ // only the configured key (supplied by the route) may be a
dynamic simple expression;
+ // a key provided through the header is used literally and never
evaluated
key = configuration.getKeyName();
+ if (key != null && hasSimpleFunction(key)) {
+ Language simple =
exchange.getContext().resolveLanguage("simple");
+ key = simple.createExpression(key).evaluate(exchange,
String.class);
+ }
}
if (key == null) {
- throw new IllegalArgumentException("AWS S3 Key header missing.");
- }
- // dynamic keys using built-in simple language
- if (hasSimpleFunction(key)) {
- Language simple = exchange.getContext().resolveLanguage("simple");
- key = simple.createExpression(key).evaluate(exchange,
String.class);
+ throw new IllegalArgumentException("AWS S3 Key is not set, or
resolved to null.");
}
return key;
}
diff --git
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3GetObjectDynamicKeyOperationIT.java
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3GetObjectDynamicKeyOperationIT.java
index 76041c38b88e..0711e4ad0252 100644
---
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3GetObjectDynamicKeyOperationIT.java
+++
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/integration/S3GetObjectDynamicKeyOperationIT.java
@@ -49,7 +49,6 @@ public class S3GetObjectDynamicKeyOperationIT extends
Aws2S3Base {
@Override
public void process(Exchange exchange) {
- exchange.getIn().setHeader(AWS2S3Constants.KEY,
"${variable.global:myVar}.txt");
exchange.getIn().setHeader(AWS2S3Constants.CONTENT_TYPE,
"application/text");
exchange.getIn().setBody("Camel rocks again!");
}
@@ -60,7 +59,6 @@ public class S3GetObjectDynamicKeyOperationIT extends
Aws2S3Base {
@Override
public void process(Exchange exchange) {
exchange.getIn().setHeader(AWS2S3Constants.BUCKET_NAME,
name.get());
- exchange.getIn().setHeader(AWS2S3Constants.KEY,
"${variable.global:myVar}.txt");
exchange.getIn().setHeader(AWS2S3Constants.S3_OPERATION,
AWS2S3Operations.getObject);
}
});
@@ -81,7 +79,9 @@ public class S3GetObjectDynamicKeyOperationIT extends
Aws2S3Base {
public void configure() {
context.setVariable("myVar", "myCamel");
- String awsEndpoint = "aws2-s3://" + name.get() +
"?autoCreateBucket=true";
+ // the dynamic key is a simple expression supplied through the
endpoint configuration (keyName)
+ String awsEndpoint
+ = "aws2-s3://" + name.get() +
"?autoCreateBucket=true&keyName=RAW(${variable.global:myVar}.txt)";
from("direct:putObject").to(awsEndpoint);
diff --git
a/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/utils/AWS2S3UtilsTest.java
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/utils/AWS2S3UtilsTest.java
new file mode 100644
index 000000000000..04681528d703
--- /dev/null
+++
b/components/camel-aws/camel-aws2-s3/src/test/java/org/apache/camel/component/aws2/s3/utils/AWS2S3UtilsTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.aws2.s3.utils;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.aws2.s3.AWS2S3Configuration;
+import org.apache.camel.component.aws2.s3.AWS2S3Constants;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class AWS2S3UtilsTest extends CamelTestSupport {
+
+ // ---- determineKey ----
+
+ @Test
+ void keyFromHeaderIsUsedLiterallyAndNotEvaluated() {
+ Exchange exchange = createExchangeWithBody("body");
+ // a key coming from the header (e.g. inherited from a consumed object
name) must be used as-is
+ exchange.getIn().setHeader(AWS2S3Constants.KEY,
"${sys.user.name}.txt");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+
+ assertEquals("${sys.user.name}.txt",
AWS2S3Utils.determineKey(exchange, config));
+ }
+
+ @Test
+ void keyFromConfigurationIsEvaluatedAsSimpleExpression() {
+ Exchange exchange = createExchangeWithBody("body");
+ exchange.getIn().setHeader("name", "report");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setKeyName("${header.name}.txt");
+
+ assertEquals("report.txt", AWS2S3Utils.determineKey(exchange, config));
+ }
+
+ @Test
+ void keyFromHeaderTakesPrecedenceOverConfiguration() {
+ Exchange exchange = createExchangeWithBody("body");
+ exchange.getIn().setHeader(AWS2S3Constants.KEY, "literal-key");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setKeyName("${header.name}.txt");
+
+ assertEquals("literal-key", AWS2S3Utils.determineKey(exchange,
config));
+ }
+
+ @Test
+ void keyMissingFromHeaderAndConfigurationThrows() {
+ Exchange exchange = createExchangeWithBody("body");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+
+ assertThrows(IllegalArgumentException.class, () ->
AWS2S3Utils.determineKey(exchange, config));
+ }
+
+ @Test
+ void keyFromConfigurationResolvingToNullThrows() {
+ Exchange exchange = createExchangeWithBody("body");
+ // a configured keyName whose simple expression resolves to null fails
fast at the producer
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setKeyName("${header.missing}");
+
+ assertThrows(IllegalArgumentException.class, () ->
AWS2S3Utils.determineKey(exchange, config));
+ }
+
+ // ---- determineBucketName ----
+
+ @Test
+ void bucketFromOverrideHeaderIsUsedLiterallyAndNotEvaluated() {
+ Exchange exchange = createExchangeWithBody("body");
+ exchange.getIn().setHeader(AWS2S3Constants.OVERRIDE_BUCKET_NAME,
"${sys.user.name}-bucket");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setBucketName("configured-bucket");
+
+ assertEquals("${sys.user.name}-bucket",
AWS2S3Utils.determineBucketName(exchange, config));
+ }
+
+ @Test
+ void bucketFromConfigurationIsEvaluatedAsSimpleExpression() {
+ Exchange exchange = createExchangeWithBody("body");
+ exchange.getIn().setHeader("env", "prod");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setBucketName("bucket-${header.env}");
+
+ assertEquals("bucket-prod", AWS2S3Utils.determineBucketName(exchange,
config));
+ }
+
+ @Test
+ void bucketOverrideHeaderTakesPrecedenceOverConfiguration() {
+ Exchange exchange = createExchangeWithBody("body");
+ exchange.getIn().setHeader(AWS2S3Constants.OVERRIDE_BUCKET_NAME,
"header-bucket");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setBucketName("configured-bucket");
+
+ assertEquals("header-bucket",
AWS2S3Utils.determineBucketName(exchange, config));
+ }
+
+ @Test
+ void bucketMissingFromHeaderAndConfigurationThrows() {
+ Exchange exchange = createExchangeWithBody("body");
+ AWS2S3Configuration config = new AWS2S3Configuration();
+
+ assertThrows(IllegalArgumentException.class, () ->
AWS2S3Utils.determineBucketName(exchange, config));
+ }
+
+ @Test
+ void bucketFromConfigurationResolvingToNullThrows() {
+ Exchange exchange = createExchangeWithBody("body");
+ // a configured bucketName whose simple expression resolves to null
fails fast at the producer
+ AWS2S3Configuration config = new AWS2S3Configuration();
+ config.setBucketName("${header.missing}");
+
+ assertThrows(IllegalArgumentException.class, () ->
AWS2S3Utils.determineBucketName(exchange, config));
+ }
+}