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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 6e54de444432 CAMEL-24241: Apply the tool argument allowlist to tools 
declaring no parameters
6e54de444432 is described below

commit 6e54de4444320eb7171c7ca3094f8501087a79b7
Author: Andrea Cosentino <[email protected]>
AuthorDate: Sat Jul 25 11:38:37 2026 +0200

    CAMEL-24241: Apply the tool argument allowlist to tools declaring no 
parameters
    
    Drop the !spec.getParameterDefs().isEmpty() guard in AiToolExecutor so
    that a tool declaring no parameters rejects all LLM-supplied arguments
    instead of letting them pass through as exchange headers. This aligns
    the shared executor with the strict form already applied in
    LangChain4jToolsProducer and SpringAiToolsEndpoint since CAMEL-23621.
    
    Add regression test verifying arguments sent to a zero-parameter tool
    do not reach exchange headers, and adjust the Camel-prefix test to use
    a tool that declares its argument names.
    
    Closes #25094
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../camel/component/ai/tool/AiToolExecutor.java    |  4 ++-
 .../component/ai/tool/AiToolExecutorTest.java      | 41 ++++++++++++++++++++--
 2 files changed, 42 insertions(+), 3 deletions(-)

diff --git 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
index 5b61d3cab8da..bacd29dbc37b 100644
--- 
a/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
+++ 
b/components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java
@@ -97,7 +97,9 @@ public final class AiToolExecutor {
 
         // Filter out undeclared arguments -- LLMs frequently hallucinate 
extra parameters
         // and the generated schema advertises additionalProperties: false.
-        if (!argsCopy.isEmpty() && !spec.getParameterDefs().isEmpty()) {
+        // A tool that declares no parameters accepts none, so an empty 
declaration must filter
+        // everything rather than let every argument through.
+        if (!argsCopy.isEmpty()) {
             Set<String> declaredParams = spec.getParameterDefs().keySet();
 
             argsCopy.keySet().removeIf(name -> {
diff --git 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
index 230f53defdc7..5b04de63623b 100644
--- 
a/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
+++ 
b/components/camel-ai/camel-ai-tool/src/test/java/org/apache/camel/component/ai/tool/AiToolExecutorTest.java
@@ -49,6 +49,16 @@ public class AiToolExecutorTest extends CamelTestSupport {
                      + "&description=A tool with no parameters")
                         .setBody(constant("no-param result"));
 
+                // declares the Camel-prefixed names so they survive the 
undeclared-argument filter
+                // and actually reach the Camel-prefix rejection this tool is 
used to test
+                from("ai-tool:prefixTool"
+                     + "?tags=test"
+                     + "&description=A tool declaring Camel-prefixed 
parameters"
+                     + "&parameter.CamelOverrideEndpoint=string"
+                     + "&parameter.camelFoo=string"
+                     + "&parameter.safeParam=string")
+                        .setBody(constant("prefix result"));
+
                 from("ai-tool:nullBodyTool"
                      + "?tags=test"
                      + "&description=A tool that returns null body")
@@ -117,6 +127,29 @@ public class AiToolExecutorTest extends CamelTestSupport {
                 .isEqualTo("no-param result");
     }
 
+    @Test
+    public void 
testExecuteIgnoresUndeclaredArgumentsForToolWithoutParameters() {
+        AiToolSpec spec = findSpec("noParams");
+        Exchange exchange = new DefaultExchange(context);
+
+        // a tool declaring no parameters accepts none, so every supplied 
argument is undeclared
+        Map<String, Object> arguments = new HashMap<>();
+        arguments.put("injected", "should-be-ignored");
+        arguments.put("anotherOne", 42);
+
+        AiToolResult result = AiToolExecutor.execute(spec, arguments, 
exchange);
+
+        assertThat(result).isInstanceOf(AiToolResult.Success.class);
+        assertThat(((AiToolResult.Success) result).value())
+                .isEqualTo("no-param result");
+        assertThat(exchange.getMessage().getHeader("injected"))
+                .as("Undeclared argument must not be set as header on a tool 
with no parameters")
+                .isNull();
+        assertThat(exchange.getMessage().getHeader("anotherOne"))
+                .as("Undeclared argument must not be set as header on a tool 
with no parameters")
+                .isNull();
+    }
+
     @Test
     public void testExecuteWithEmptyArguments() {
         AiToolSpec spec = findSpec("noParams");
@@ -236,7 +269,9 @@ public class AiToolExecutorTest extends CamelTestSupport {
 
     @Test
     public void testCamelPrefixedArgumentsRejectedFromHeaders() {
-        AiToolSpec spec = findSpec("noParams");
+        // prefixTool declares these names, so they reach the Camel-prefix 
check rather than being
+        // dropped earlier as undeclared -- otherwise this test would pass 
even without that check
+        AiToolSpec spec = findSpec("prefixTool");
         Exchange exchange = new DefaultExchange(context);
 
         Map<String, Object> arguments = new HashMap<>();
@@ -253,11 +288,13 @@ public class AiToolExecutorTest extends CamelTestSupport {
         assertThat(exchange.getMessage().getHeader("camelFoo"))
                 .as("camel-prefixed argument must not be set as header")
                 .isNull();
+        // a dotted name cannot be declared through the endpoint URI (the key 
is split on the first
+        // dot), so this one is dropped as an undeclared argument rather than 
by the prefix check
         assertThat(exchange.getMessage().getHeader("org.apache.camel.hack"))
                 .as("org.apache.camel. prefixed argument must not be set as 
header")
                 .isNull();
         assertThat(exchange.getMessage().getHeader("safeParam", String.class))
-                .as("Non-Camel argument should be set as header")
+                .as("Declared non-Camel argument should be set as header")
                 .isEqualTo("ok");
     }
 

Reply via email to