You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Claus Ibsen (Jira)" <ji...@apache.org> on 2026/09/28 08:03:00 UTC
[jira] [Updated] (CAMEL-25033) camel-bean: a parameter bound from ${body} or ${header.x} in the method name loses its surrounding quotes, and the text "null" becomes a Java null
[ https://issues.apache.org/jira/browse/CAMEL-25033?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Claus Ibsen updated CAMEL-25033:
--------------------------------
Fix Version/s: 4.23.0
> camel-bean: a parameter bound from ${body} or ${header.x} in the method name loses its surrounding quotes, and the text "null" becomes a Java null
> --------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25033
> URL: https://issues.apache.org/jira/browse/CAMEL-25033
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Priority: Minor
> Fix For: 4.23.0
>
>
> The method option can bind parameters from Simple expressions, for example {{bean(MyService.class, "process(${body})")}} or {{to("bean:foo?method=check(${header.ETag})")}}. {{bean-binding.adoc}} ("Parameter binding using method option") documents two different kinds of value:
> * a String literal in quotes, such as {{'World'}}, is passed "without quotes";
> * a Simple token, such as {{${header.high}}}, is passed as the value of that header.
> {{MethodInfo.ParameterExpression}} does not keep them apart. It evaluates every parameter with the Simple language and then post-processes the *result*:
> {code:java}
> // MethodInfo.java:728
> if ("null".equals(parameterValue)) {
> return Void.TYPE; // the parameter becomes null
> }
> ...
> // MethodInfo.java:789-793 convertParameterValue
> if (parameterValue instanceof String string) {
> parameterValue = StringHelper.removeLeadingAndEndingQuotes(string);
> }
> {code}
> {{removeLeadingAndEndingQuotes}} ({{StringHelper.java:161-179}}) trims the value and removes the first and last char when both are {{'}} or both are {{"}}. So the quote handling that is meant for the literal text of the method name is also applied to message data:
> * An HTTP {{ETag}} / {{If-None-Match}} value {{"33a64df5"}} reaches the method as {{33a64df5}}.
> * A CSV line {{"ACME, Inc.","42"}} (for example after {{split(body().tokenize("\n"))}}) reaches the method as {{ACME, Inc.","42}}.
> * A JSON string body {{"text"}} loses its quotes. A body {{'quoted'}} becomes {{quoted}}, and {{ 'x' }} becomes {{x}}.
> * A header or body whose value is the four-letter text {{null}} is passed as Java {{null}}.
> There is no error or log. The method gets different data than the message holds.
> *Reproduction* (standalone program against main dbdd4b381, bean {{echo(String s)}} returns {{[s]}}):
> {noformat}
> echo(${header.v}) header v = "33a64df551425fcc55e4d42a148795d9" -> [33a64df551425fcc55e4d42a148795d9] expected ["33a64df551425fcc55e4d42a148795d9"]
> to("bean:echo?method=echo(${header.v})") same header -> same result
> echo(${body}) body "ACME, Inc.","42" -> [ACME, Inc.","42] expected ["ACME, Inc.","42"]
> echo(${body}) body 'quoted' -> [quoted]
> echo(${body}) body "just a JSON string" -> [just a JSON string]
> echo(${body}) body " 'x' " -> [x]
> echo(${body}) body '' -> []
> two(${header.a}, ${header.b}) a='x' b="y" -> [x|y] expected ['x'|"y"]
> echo(${header.v}) header v = null (the text) -> method receives null expected [null]
> controls: echo (no parameters in the method name) with the ETag body -> ["33a…"]; echo('World') -> [World];
> echo(${header.v}) with v = abc -> [abc]
> workaround: echo('${header.v}') with the ETag header -> ["33a…"], with v = null (the text) -> [null]
> {noformat}
> A property-based test (jqwik) over random header values fails for every quoted value (shrunk sample {{''}}) and, over an alphabet with quotes and the letters of {{null}}, shrinks to {{'aaaa'}}. The control property (letters only) passes 500 tries.
> A Lean model proves the following:
> * For every string {{w}}, a {{${...}}} value {{"w"}} or {{'w'}} is bound as {{w}} ({{dquoted_value_corrupted}}, {{squoted_value_corrupted}}), so {{w}} and {{'w'}} cannot be told apart ({{not_injective}}).
> * A value that {{removeLeadingAndEndingQuotes}} leaves unchanged and that is not the text {{null}} is bound correctly today ({{unchanged_value_ok}}), and literal parameters are already correct ({{literal_ok}}). The defect is exactly: quoted data and the text {{null}}.
> * The fix below, which decides on the parameter text instead of the evaluated value, meets the documented behaviour for every token ({{fix_meets_spec}}).
> The same {{removeLeadingAndEndingQuotes}} call after the Simple evaluation is in camel-2.25.4 ({{MethodInfo.java:798}}), camel-3.0.0 ({{:719}}), camel-4.0.0 ({{:713}}) and camel-4.14.0 ({{:762}}), so all maintained versions are affected.
> *Proposed fix:* decide what to do from the parameter *text* of the method name, before evaluating it:
> {code:java}
> String exp = ...; // the parameter text, e.g. 'World', ${header.x}, null, 5
> boolean nullKeyword = "null".equals(exp.trim());
> boolean quotedLiteral = StringHelper.isQuoted(exp.trim());
> if (nullKeyword) {
> return Void.TYPE;
> }
> if (quotedLiteral) {
> exp = StringHelper.removeLeadingAndEndingQuotes(exp); // the quotes belong to the method name syntax
> }
> Object parameterValue = evaluateSimpleExpression(exchange, index, exp);
> // no removeLeadingAndEndingQuotes and no "null" check on parameterValue
> {code}
> Then {{'World'}} gives {{World}}, {{'${body}'}} gives the body, {{${body}}} gives the body unchanged, and {{null}} still passes {{null}}. A {{${...}}} that evaluates to Java {{null}} keeps passing {{null}} (use a marker object instead of the String {{"null"}} in {{evaluateSimpleExpression}}).
> Tests: {{BeanParameterValueTest}} with {{echo(${header.v})}} for the header values {{"abc"}}, {{'abc'}} and {{null}} (the text); they must reach the method unchanged. The existing literal tests ({{bar('Camel', true)}}, {{doSomething('Hello World', 2)}}, {{put('isMaster','true')}}) must keep passing.
> Related, lower severity (same parser, could be fixed in the same change): {{StringQuoteHelper.splitSafeQuote}} ({{MethodInfo.java:612}}) splits at every comma outside quotes, including commas inside {{${...}}}. {{echo(${body.substring(0, 3)})}} and {{two(${body}, ${header.v.replace('a', 'b')})}} fail with {{SimpleParserException: missing } to close the function}}. CAMEL-24967 fixed the same problem for Simple function arguments with a depth-aware splitter.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)