[
https://issues.apache.org/jira/browse/CAMEL-24241?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen updated CAMEL-24241:
--------------------------------
Fix Version/s: 4.22.0
> camel-ai-tool: AiToolExecutor skips the argument allowlist for tools
> declaring no parameters
> --------------------------------------------------------------------------------------------
>
> Key: CAMEL-24241
> URL: https://issues.apache.org/jira/browse/CAMEL-24241
> Project: Camel
> Issue Type: Bug
> Reporter: Andrea Cosentino
> Assignee: Andrea Cosentino
> Priority: Major
> Fix For: 4.22.0
>
>
> CAMEL-23382 ({{bfd60dfb361e}}) moved the LLM-tool-argument handling out of
> {{LangChain4jAgentProducer}} into the new shared {{AiToolExecutor}}. In the
> process, the argument allowlist regained a bypass that CAMEL-23621 had
> deliberately removed.
> h3. Current code
> {{components/camel-ai/camel-ai-tool/src/main/java/org/apache/camel/component/ai/tool/AiToolExecutor.java}}:
> {noformat}
> if (!argsCopy.isEmpty() && !spec.getParameterDefs().isEmpty()) {
> Set<String> declaredParams = spec.getParameterDefs().keySet();
> argsCopy.keySet().removeIf(name -> {
> if (!declaredParams.contains(name)) { ...filter... }
> });
> }
> {noformat}
> When {{spec.getParameterDefs()}} is empty, the whole filtering block is
> skipped. The loop that follows then sets every remaining argument as an
> exchange header.
> A tool that declares no parameters is entirely legal — a {{description}}
> alone is sufficient. For such a tool, the LLM can therefore cause arbitrary
> header names (of its choosing) to be set on the exchange that runs the tool
> route.
> h3. Why this is a regression
> Commit {{e9c4541a91ce}}, titled "CAMEL-23621: remove backwards-compatibility
> bypass and fix raw JsonNode headers", removed exactly this pattern from three
> places:
> {noformat}
> - if (!allowedParams.isEmpty() && !allowedParams.contains(name)) {
> + if (!allowedParams.contains(name)) {
> {noformat}
> (in {{LangChain4jAgentProducer}}, {{LangChain4jToolsProducer}} and
> {{SpringAiToolsEndpoint}}).
> The two producers still have the strict form today. Only the new shared
> executor reintroduced the {{isEmpty()}} guard.
> h3. Severity — hardening, not a CVE
> {{AiToolExecutor}} does keep a case-insensitive rejection of names starting
> with {{camel}} or {{org.apache.camel.}}, so the CVE-2025-27636
> header-injection family is *not* reachable through this path. What is
> unfiltered is arbitrary *non-Camel* header names originating from model
> output.
> That still matters: LLM output is attacker-influenceable through prompt
> injection, and tool routes commonly forward headers to downstream components
> (HTTP producers, SQL named parameters, etc.). The route author's declared
> parameter set is the intended contract, and for zero-parameter tools it is
> currently not enforced.
> h3. Suggested fix
> Drop the {{&& !spec.getParameterDefs().isEmpty()}} condition so an empty
> declaration means "no arguments are allowed" rather than "all arguments are
> allowed" — matching what CAMEL-23621 established for the sibling producers:
> {noformat}
> if (!argsCopy.isEmpty()) {
> Set<String> declaredParams = spec.getParameterDefs().keySet();
> ...
> }
> {noformat}
> A test covering a zero-parameter tool that receives unexpected arguments
> would lock the behaviour in; the existing tests appear to cover only tools
> that declare parameters.
> h3. Related
> Found while reviewing PR #24992 (CAMEL-23944). Not caused by that PR — it
> patches the pre-CAMEL-23382 code and is unrelated.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)