Skip to content

CAMEL-24241: Apply the tool argument allowlist to tools declaring no parameters - #25094

Merged
davsclaus merged 1 commit into
apache:mainfrom
oscerd:fix/CAMEL-24241-aitool-allowlist-empty-params
Jul 25, 2026
Merged

CAMEL-24241: Apply the tool argument allowlist to tools declaring no parameters#25094
davsclaus merged 1 commit into
apache:mainfrom
oscerd:fix/CAMEL-24241-aitool-allowlist-empty-params

Conversation

@oscerd

@oscerd oscerd commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

The problem

AiToolExecutor gates the undeclared-argument filter on the parameter map being non-empty:

if (!argsCopy.isEmpty() && !spec.getParameterDefs().isEmpty()) {
    Set<String> declaredParams = spec.getParameterDefs().keySet();
    argsCopy.keySet().removeIf(name -> { ... });   // filter undeclared
}

So a tool that declares no parameters skips filtering entirely, and every argument the model sends becomes an exchange header.

A tool declaring no parameters accepts none. AiToolParameterHelper.buildJsonSchemaFromDefs emits "additionalProperties": false unconditionally, so the schema such a tool advertises already forbids every property — which is exactly the rationale the filter's own comment cites.

This also re-introduced a bypass that was removed on purpose. Commit e9c4541a91ce ("CAMEL-23621: remove backwards-compatibility bypass") stripped the identical isEmpty() guard from three places:

-    if (!allowedParams.isEmpty() && !allowedParams.contains(name)) {
+    if (!allowedParams.contains(name)) {

LangChain4jToolsProducer and SpringAiToolsEndpoint still carry the strict form; only the newer shared executor regressed it.

Severity

Hardening rather than a CVE. The Camel-internal prefix rejection (camel / org.apache.camel., case-insensitive) still applies, 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 — and LLM output is attacker-influenceable via prompt injection, while tool routes commonly forward headers to downstream components (HTTP producers, SQL named parameters).

The fix

Drop the && !spec.getParameterDefs().isEmpty() condition, so an empty declaration means "no arguments accepted" instead of "all arguments accepted".

Tests

  • New: testExecuteIgnoresUndeclaredArgumentsForToolWithoutParameters — arguments sent to a zero-parameter tool must not reach the exchange headers. Verified it fails without the fix (expected: null but was: "should-be-ignored") and passes with it.

  • Adjusted: testCamelPrefixedArgumentsRejectedFromHeaders used the noParams tool and depended on the bypass to get its non-Camel argument through, so the fix broke it. Simply flipping that assertion to isNull() would have made the test vacuous — all four arguments would then be dropped by the allowlist, and it would pass even if the Camel-prefix check were deleted. Instead it now points at a new prefixTool that declares those names, so the Camel-prefixed arguments survive the allowlist and genuinely exercise the prefix check.

    One caveat recorded in a comment: a dotted name such as org.apache.camel.hack cannot be declared through the endpoint URI (parseParameterMetadata splits the key on the first dot), so that one is dropped as undeclared rather than by the prefix check.

Full AiToolExecutorTest suite: 13 tests, green.

Upgrade guide

Not required — camel-ai-tool is new in 4.22 (CAMEL-23382) and the permissive behaviour has never appeared in a release.


Claude Code on behalf of @oscerd

…parameters

AiToolExecutor gated the undeclared-argument filter on
!spec.getParameterDefs().isEmpty(), so a tool that declares no parameters
skipped filtering entirely and every LLM-supplied argument became an exchange
header. A tool declaring no parameters accepts none: buildJsonSchemaFromDefs
emits "additionalProperties": false unconditionally, so the schema such a tool
advertises already forbids every property.

This re-introduced the bypass that CAMEL-23621 (e9c4541, "remove
backwards-compatibility bypass") deliberately removed from the sibling
producers, which still carry the strict form.

LLM output is attacker-influenceable through prompt injection, and tool routes
commonly forward headers to downstream components. The Camel-internal prefix
rejection still applied, so the CVE-2025-27636 header-injection family was not
reachable; what was unfiltered is arbitrary non-Camel header names.

Add a regression test asserting that arguments sent to a zero-parameter tool do
not reach the exchange headers.

testCamelPrefixedArgumentsRejectedFromHeaders used the noParams tool and relied
on the bypass to get its non-Camel argument through. Point it at a new
prefixTool that declares those names, so the Camel-prefixed arguments survive
the allowlist and actually reach the prefix check the test exists to verify --
otherwise it would pass even if that check were deleted.

No upgrade guide entry: camel-ai-tool is new in 4.22 (CAMEL-23382) and the
permissive behaviour has never been released.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@oscerd
oscerd requested a review from gnodet July 24, 2026 13:13
@oscerd oscerd added the bug Something isn't working label Jul 24, 2026
@oscerd
oscerd requested a review from davsclaus July 24, 2026 13:13

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-crafted hardening fix. The bypass regression is confirmed against e9c4541a91ce (CAMEL-23621), and the fix aligns the shared executor with the sibling producers that already carry the strict form.

One minor convention nit on test visibility (inline below), otherwise LGTM.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, minimal, and well-reasoned security hardening fix. Removes the !spec.getParameterDefs().isEmpty() bypass so that zero-parameter tools correctly reject all LLM-hallucinated arguments instead of letting them pass through as exchange headers.

AiToolSpec.getParameterDefs() is guaranteed non-null (returns Map.of() if constructor argument is null), so no NPE risk. The fix is consistent with the strict enforcement already applied in LangChain4jToolsProducer and SpringAiToolsEndpoint (commit e9c4541). The additionalProperties: false schema emission confirms server-side enforcement is the correct complement.

Test coverage is thorough and the existing test was carefully adjusted. The component is new in 4.22 with no release tags, so no upgrade guide is needed.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-ai/camel-ai-tool

🔬 Scalpel shadow comparison — Scalpel: 11 tested, 29 compile-only — current: 11 all tested

Maveniverse Scalpel detected 40 affected modules (current approach: 11).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 11 modules (1 direct + 10 downstream), skip tests for 29 (generated code, meta-modules)

Modules Scalpel would test (11)
  • camel-ai-tool
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-langchain4j-agent
  • camel-launcher-container
  • camel-spring-ai-chat
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (40 modules)
  • Camel :: AI :: LangChain4j :: Agent
  • Camel :: AI :: Tool
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Spring AI :: Chat
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@davsclaus davsclaus added this to the 4.22.0 milestone Jul 25, 2026
@davsclaus
davsclaus merged commit 6e54de4 into apache:main Jul 25, 2026
5 checks passed
@oscerd
oscerd deleted the fix/CAMEL-24241-aitool-allowlist-empty-params branch July 27, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working components components-ai

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants