shashank created CAMEL-25033:
--------------------------------

             Summary: 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


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)

Reply via email to