Andrea Cosentino created CAMEL-24241:
----------------------------------------
Summary: 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
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)