This is an automated email from the ASF dual-hosted git repository.

Croway 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 5d0028f6bc7a CAMEL-23621: filter tool argument headers against 
declared parameters
5d0028f6bc7a is described below

commit 5d0028f6bc7a70556dc1d408b1b6cadb59e1842d
Author: Croway <[email protected]>
AuthorDate: Wed May 27 10:23:52 2026 +0200

    CAMEL-23621: filter tool argument headers against declared parameters
    
    Backport of #23535 to camel-4.18.x. Filters LLM tool argument field
    names against the tool's declared parameter schema before setting them
    as Exchange headers, preventing prompt-injection attacks from injecting
    arbitrary Camel control headers.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../agent/LangChain4jAgentProducer.java            | 31 +++++++++++++++++++++-
 .../tools/LangChain4jToolsProducer.java            | 13 +++++++++
 .../langchain4j/tools/LangChain4jToolTest.java     | 29 ++++++++++++++++++++
 .../springai/tools/SpringAiToolsEndpoint.java      | 14 +++++++++-
 .../ROOT/pages/camel-4x-upgrade-guide-4_18.adoc    | 24 +++++++++++++++++
 5 files changed, 109 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
 
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
index 41fc57ea0001..a18a3377aad5 100644
--- 
a/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
+++ 
b/components/camel-ai/camel-langchain4j-agent/src/main/java/org/apache/camel/component/langchain4j/agent/LangChain4jAgentProducer.java
@@ -22,6 +22,7 @@ import java.util.stream.Collectors;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import dev.langchain4j.agent.tool.ToolSpecification;
+import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
 import dev.langchain4j.service.tool.ToolExecutor;
 import dev.langchain4j.service.tool.ToolProvider;
 import dev.langchain4j.service.tool.ToolProviderRequest;
@@ -129,9 +130,37 @@ public class LangChain4jAgentProducer extends 
DefaultProducer {
                         // Parse JSON arguments if provided
                         String arguments = toolExecutionRequest.arguments();
                         if (arguments != null && !arguments.trim().isEmpty()) {
+                            // Get declared parameters from tool specification 
to filter incoming fields
+                            Set<String> declaredParams = Set.of();
+                            JsonObjectSchema paramSchema = 
toolSpecification.parameters();
+                            if (paramSchema != null && 
paramSchema.properties() != null) {
+                                declaredParams = 
paramSchema.properties().keySet();
+                            }
+                            final Set<String> allowedParams = declaredParams;
+
                             JsonNode jsonNode = 
objectMapper.readValue(arguments, JsonNode.class);
                             jsonNode.fieldNames()
-                                    .forEachRemaining(name -> 
exchange.getMessage().setHeader(name, jsonNode.get(name)));
+                                    .forEachRemaining(name -> {
+                                        if (!allowedParams.contains(name)) {
+                                            LOG.warn("Skipping undeclared tool 
argument '{}' for tool '{}'",
+                                                    name, toolName);
+                                            return;
+                                        }
+                                        JsonNode value = jsonNode.get(name);
+                                        Object headerValue;
+                                        if (value.isInt()) {
+                                            headerValue = value.intValue();
+                                        } else if (value.isLong()) {
+                                            headerValue = value.longValue();
+                                        } else if (value.isDouble()) {
+                                            headerValue = value.doubleValue();
+                                        } else if (value.isBoolean()) {
+                                            headerValue = value.booleanValue();
+                                        } else {
+                                            headerValue = value.asText();
+                                        }
+                                        exchange.getMessage().setHeader(name, 
headerValue);
+                                    });
                         }
 
                         // Set the tool name as a header for route 
identification
diff --git 
a/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
 
b/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
index 522609ee9424..994df32272ab 100644
--- 
a/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
+++ 
b/components/camel-ai/camel-langchain4j-tools/src/main/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolsProducer.java
@@ -173,10 +173,23 @@ public class LangChain4jToolsProducer extends 
DefaultProducer {
             try {
                 TypeConverter typeConverter = 
endpoint.getCamelContext().getTypeConverter();
 
+                // Get declared parameters from tool specification to filter 
incoming fields
+                Set<String> declaredParams = Set.of();
+                JsonObjectSchema paramSchema = 
camelToolSpecification.getToolSpecification().parameters();
+                if (paramSchema != null && paramSchema.properties() != null) {
+                    declaredParams = paramSchema.properties().keySet();
+                }
+                final Set<String> allowedParams = declaredParams;
+
                 // Map Json to Header
                 JsonNode jsonNode = 
objectMapper.readValue(toolExecutionRequest.arguments(), JsonNode.class);
                 jsonNode.fieldNames()
                         .forEachRemaining(name -> {
+                            if (!allowedParams.contains(name)) {
+                                LOG.warn("Skipping undeclared tool argument 
'{}' for tool '{}'",
+                                        name, toolName);
+                                return;
+                            }
                             final JsonNode value = jsonNode.get(name);
                             Object headerValue;
 
diff --git 
a/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
 
b/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
index 06ca58cd8f14..1b45702d9da7 100644
--- 
a/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
+++ 
b/components/camel-ai/camel-langchain4j-tools/src/test/java/org/apache/camel/component/langchain4j/tools/LangChain4jToolTest.java
@@ -56,6 +56,12 @@ public class LangChain4jToolTest extends CamelTestSupport {
             .withParam("tags", "users")
             .andThenInvokeTool("queryUserBySSN")
             .withParam("ssn", "123-45-6789")
+            .end()
+            .when("Query user 5\n")
+            .invokeTool("QueryUserByNumber")
+            .withParam("number", 5)
+            .withParam("CamelFileName", "../../etc/passwd")
+            .withParam("undeclaredParam", "injected")
             .build();
 
     @Override
@@ -172,6 +178,29 @@ public class LangChain4jToolTest extends CamelTestSupport {
         Assertions.assertThat(response).isNotNull();
     }
 
+    @Test
+    public void testUndeclaredToolArgumentsAreNotPropagatedAsHeaders() {
+        List<ChatMessage> messages = new ArrayList<>();
+        messages.add(new SystemMessage("You provide the requested information 
using the functions you have available."));
+        messages.add(new UserMessage("Query user 5\n"));
+
+        Exchange exchange = 
fluentTemplate.to("direct:test").withBody(messages).request(Exchange.class);
+
+        Assertions.assertThat(exchange).isNotNull();
+        Message message = exchange.getMessage();
+
+        // Declared parameter should be set
+        Assertions.assertThat(message.getHeader("number")).isEqualTo(5);
+
+        // Undeclared parameters should NOT be propagated as headers
+        Assertions.assertThat(message.getHeader("CamelFileName"))
+                .as("Undeclared 'CamelFileName' should not be set as header")
+                .isNull();
+        Assertions.assertThat(message.getHeader("undeclaredParam"))
+                .as("Undeclared 'undeclaredParam' should not be set as header")
+                .isNull();
+    }
+
     @Test
     public void testSearchAndUseNonExposedTool() throws Exception {
         List<ChatMessage> messages = new ArrayList<>();
diff --git 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java
 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java
index 21e1209f1544..cc99012809a5 100644
--- 
a/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java
+++ 
b/components/camel-spring-parent/camel-spring-ai/camel-spring-ai-tools/src/main/java/org/apache/camel/component/springai/tools/SpringAiToolsEndpoint.java
@@ -20,6 +20,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
@@ -120,12 +121,23 @@ public class SpringAiToolsEndpoint extends 
DefaultEndpoint {
         final SpringAiToolsConsumer springAiToolsConsumer = new 
SpringAiToolsConsumer(this, processor);
         configureConsumer(springAiToolsConsumer);
 
+        // Get declared parameter names to filter incoming arguments
+        final Set<String> declaredParams;
+        if (parameters != null && !parameters.isEmpty()) {
+            declaredParams = parseParameterMetadata(parameters).keySet();
+        } else {
+            declaredParams = Set.of();
+        }
+
         // Create a function that executes the Camel route
         java.util.function.Function<java.util.Map<String, Object>, String> 
function = args -> {
             try {
                 org.apache.camel.Exchange exchange = createExchange();
-                // Set arguments as headers
+                // Set arguments as headers, filtered against declared 
parameters
                 for (java.util.Map.Entry<String, Object> entry : 
args.entrySet()) {
+                    if (!declaredParams.contains(entry.getKey())) {
+                        continue;
+                    }
                     exchange.getMessage().setHeader(entry.getKey(), 
entry.getValue());
                 }
 
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
index 78a743445589..2553ea29377e 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
@@ -13,6 +13,30 @@ See the xref:camel-upgrade-recipes-tool.adoc[documentation] 
page for details.
 
 == Upgrading from 4.18.2 to 4.18.3
 
+=== camel-langchain4j-tools / camel-langchain4j-agent / camel-spring-ai-tools
+
+LLM tool argument field names are now filtered against the tool's declared 
parameter schema
+before being set as Exchange headers. Only arguments whose field names match a 
declared
+`parameter.<name>` are propagated; undeclared fields are logged at WARN level 
and skipped.
+
+This is a security hardening measure to prevent prompt-injection attacks from 
injecting
+arbitrary Camel control headers (such as `CamelFileName`, `CamelSqlQuery`, 
`CamelHttpUri`)
+via crafted tool call arguments.
+
+**Action required:** If your tool consumer routes rely on LLM-provided 
arguments being set as
+Exchange headers, you must explicitly declare those parameters in the endpoint 
URI. For example:
+
+[source,java]
+----
+// Before (all LLM arguments become headers — vulnerable):
+from("langchain4j-tools:myTool?tags=mytag&description=My tool")
+
+// After (only declared parameters become headers):
+from("langchain4j-tools:myTool?tags=mytag&description=My 
tool&parameter.city=string&parameter.country=string")
+----
+
+Tools that already declare their parameters via `parameter.<name>=<type>` are 
unaffected.
+
 === camel-aws-bedrock
 
 The `applyGuardrail` producer operation now reads the guardrail identifier 
from a new dedicated header

Reply via email to