CAMEL-24576: camel-dynamic-router - do not take the subscription predicate from the control message by default - #25992
Conversation
…icate from the control message by default The dynamic-router-control producer built subscriptions entirely from the incoming message, resolving whichever expression language the message named and compiling the supplied expression into a filter kept in the channel's rule base. Taking a template or script from the message is an explicit opt-in everywhere else in Camel (allowTemplateFromHeader), but the control endpoint had no equivalent. Add allowPredicateFromMessage to the control endpoint, default false and annotated security = "insecure:dev". When it is false, a control message supplying a predicate or an expressionLanguage is rejected, and the values configured on the endpoint are used instead. The predicateBean path and the JMX operations are unchanged. The flag is deliberately absent from URI_PARAMS_TO_HEADER_NAMES so that it can never be set from a message; resolveStaticUri keeps it on the optimised endpoint URI instead, since toD otherwise strips it and the endpoint would silently fall back to false. Subscription parameters now fall back to the endpoint configuration when the message does not carry them, which makes the documented URI-parameter form work outside toD. Also expose allowedSchemes on the dynamic-router endpoint and wire it into the RecipientList built by DynamicRouterRecipientListHelper, which never called setAllowedSchemes. It is unset by default, matching the previous behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xhhhvd8YPrPXuXxL1ULdt Signed-off-by: Andrea Cosentino <ancosen@gmail.com>
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
davsclaus
left a comment
There was a problem hiding this comment.
Reviewed against the project's build/test/formatting/security-annotation conventions (CLAUDE.md) and against CAMEL-24576, which this PR implements exactly.
Build/test verification: Merged the PR branch into a clean worktree and ran the full module suite directly — 134 unit tests and 21 integration tests (mvn verify -Pit) pass, matching the numbers in the PR description. formatter:validate/impsort:check are clean, and regenerating the catalog/DSL sources produces zero drift, so CI's uncommitted-changes gate should pass.
Design/security review:
- The
allowPredicateFromMessagegate correctly rejects a message-suppliedpredicate/expressionLanguagewhenfalse(default) and falls back to the endpoint-configured value; well covered by new tests. predicateBeanis correctly left untouched by the gate.resolveStaticUrideliberately excludesallowPredicateFromMessagefromURI_PARAMS_TO_HEADER_NAMESand instead retains it on the statictoD-optimized URI — verified this is the only property handled that way, and it's exactly right: it's the one flag that must never become a settable header, and the one that would otherwise silently revert tofalseundertoD. Nicely caught and tested (resolveStaticUriRetainsAllowPredicateFromMessage,DynamicRouterSendDynamicAwareIT).allowedSchemeswiring intoDynamicRouterRecipientListHelper/RecipientListcorrectly mirrors the CAMEL-24298 precedent for the sibling recipientList/toD family.security = "insecure:dev"annotation and theSecurityUtilsmap entry are present and correctly categorized per the project's insecure-flag convention.- Docs, catalog mirror, and the 4.23 upgrade guide entry (on
main, as required) are all complete and consistent.
One non-blocking design question is left as an inline comment below, on predicateValueToUse. Everything else is solid — approving.
This review is a rules-and-conventions check plus a manual build/test verification per the project's CLAUDE.md and OSS Helper standards — it does not replace SonarCloud, CodeRabbit, or similar static-analysis/review tools.
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
gnodet
left a comment
There was a problem hiding this comment.
Well-designed security fix that prevents the dynamic-router-control endpoint from taking untrusted predicate expressions from incoming messages by default. The approach follows established Camel security patterns (allowTemplateFromHeader in camel-language, ResourceEndpoint-based components).
Key design points that are correct:
allowPredicateFromMessagedefaults tofalseand is annotatedsecurity = "insecure:dev"(socamel.main.profile = prodblocks it)- Excluded from
URI_PARAMS_TO_HEADER_NAMESso it cannot be set from a message predicateBeanpath correctly unchanged — it selects from route-author-bound registry beans- JMX operations unchanged — operator territory
allowedSchemesaddition properly wires into theRecipientListresolveStaticUricorrectly retains the option on optimized endpoint URIs
Test coverage is thorough and the upgrade guide entry clearly explains the breaking change.
📋 PR Metadata
| Aspect | Current | Suggested |
|---|---|---|
| Milestone | (none) | 4.23.0 |
🔀 Backport Status
main but no backport PRs were found. Note that a straight backport would change the default on LTS branches — consider shipping the option without changing the default, or using a deprecation warning on maintenance branches:
camel-4.22.xcamel-4.18.x
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
|
🧪 CI tested the following changed modules:
🔬 Scalpel shadow comparison — Scalpel: 573 tested, 24 compile-only — current: 570 all testedMaveniverse Scalpel detected 597 affected modules (current approach: 570).
|
|
This PR is otherwise ready to merge (CI green, 2 approvals, milestone/labels set), but it now has a merge conflict against @oscerd could you rebase onto Claude Code on behalf of davsclaus |
Adds allowPredicateFromMessage on the dynamic-router-control endpoint, default false. Previously a control message could freely supply a predicate/expressionLanguage that got compiled into the channel's rule base -- untrusted input driving expression evaluation, unlike every other Camel opt-in-gated template/script source (allowTemplateFromHeader). When the gate is closed, control messages fall back to the endpoint's configured predicate instead of being rejected. Also fixes DynamicRouterRecipientListHelper to wire allowedSchemes into the RecipientList it builds, and makes subscribeFromHeaders fall back to endpoint configuration for subscription parameters the message omits. This changes a default: deployments relying on control messages supplying their own predicate must now set allowPredicateFromMessage=true or switch to predicateBean. Documented in the 4.23 upgrade guide. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Closes #25992 (cherry picked from commit 1d5d3e9)
…r backports (#26020) The upgrade guides for every release line live on main, so the backports of #25992 to camel-4.22.x (#26018) and camel-4.18.x (#26019) carry no guide edit. Add the matching entries here. The 4.18 entry notes that the line does not carry allowedSchemes or the insecure:dev marker, since neither exists on that branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xhhhvd8YPrPXuXxL1ULdt Signed-off-by: Andrea Cosentino <ancosen@gmail.com>
Backport of #25992 onto camel-4.18.x, reduced to the control endpoint gate. This branch has no security marker attributes on @UriParam, no SecurityUtils insecure-options registry, and no RecipientList.setAllowedSchemes, so the insecure:dev marker and the allowedSchemes option are left out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xhhhvd8YPrPXuXxL1ULdt Signed-off-by: Andrea Cosentino <ancosen@gmail.com>
Backport of #25992 onto camel-4.22.x. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xhhhvd8YPrPXuXxL1ULdt Signed-off-by: Andrea Cosentino <ancosen@gmail.com>
Fixes CAMEL-24576.
The
dynamic-router-controlproducer built subscriptions entirely from values carried on the incomingmessage.
subscribeFromMessageandsubscribeFromHeadersread every parameter from the message bodyor headers, and
DynamicRouterControlService.obtainPredicateFromExpressionthen resolved whicheverlanguage the message named and compiled the supplied expression into a filter retained in the
channel's rule base.
Elsewhere in Camel, taking a template or script from the message is an explicit opt-in that defaults
to off —
allowTemplateFromHeaderoncamel-languageand on theResourceEndpoint-based templatecomponents. The control endpoint had no equivalent, so a route author had no way to say that the
predicate should come from the endpoint rather than from message content.
Changes
allowPredicateFromMessageon the control endpoint, defaultfalse. When it isfalse, acontrol message that supplies a
predicateor anexpressionLanguageis rejected with anIllegalArgumentException, and the values configured on the endpoint are used instead. The optionis annotated
security = "insecure:dev", so undercamel.main.profile = prodan endpoint thatenables it will not start unless
camel.security.insecureDevPolicyis relaxed.The
predicateBeanpath is deliberately unchanged: it selects aPredicatethat the route authorbound in the registry, so the message only picks from what the author already provided. The JMX
operations on
DynamicRouterControlServiceare also unchanged — management access is operatorterritory, and the gate belongs on the message path.
resolveStaticUrikeepsallowPredicateFromMessageon the optimised URI.toDstrips the wholequery and replays the subscription parameters as headers. The flag must not travel that way, or it
would become settable from a message, so it is deliberately absent from
URI_PARAMS_TO_HEADER_NAMESand is instead retained on the static endpoint URI, where only the route author can set it. Without
this the flag silently reverted to
falseundertoD— caught byDynamicRouterSendDynamicAwareIT.Endpoint fallback for the remaining subscription parameters.
subscribeFromHeadersneverconsulted the endpoint configuration, so the URI-parameter form documented for the control component
only ever worked through
toD. Parameters now fall back to the configured values when the messagedoes not carry them, which also gives a participant a way to send only what identifies it and let the
endpoint supply the predicate.
allowedSchemeson thedynamic-routerendpoint.DynamicRouterRecipientListHelperbuilt thecomponent's
RecipientListwithout ever callingsetAllowedSchemes, so the allow-list added for therecipientList/toD family in CAMEL-24298 did not reach a subscription's
destinationUri. The optionis unset by default, which allows any scheme and matches the previous behaviour.
Compatibility
This changes a default. A deployment that today lets control messages carry their own predicate — the
multi-JVM bridge shown in the component documentation — must now set
allowPredicateFromMessage=trueon the control endpoint, or switch those subscriptions to
predicateBean. Documented in the 4.23upgrade guide.
Tests
mvn verifyoncomponents/camel-dynamic-routeris green: 134 unit tests and 21 integration tests.New coverage in
DynamicRouterControlProducerTest: the predicate and the expression language are eachrejected from headers and from a control-message body, on both
subscribeandupdate; theendpoint-configured predicate is used when the message supplies none; all subscription parameters fall
back to the endpoint; and
predicateBeanstill works with the gate closed.DynamicRouterControlChannelSendDynamicAwareTestcovers the flag surviving
resolveStaticUri, andDynamicRouterRecipientListHelperTestcovers theallowedSchemeswiring. The integration tests that deliberately let the control message supply thepredicate now opt in.
Assertions follow the JUnit style already used throughout this module —
camel-dynamic-routerdoesnot depend on AssertJ, and adding the dependency for a handful of new assertions did not seem worth it
here.
Claude Code on behalf of oscerd