shashank created CAMEL-25453:
--------------------------------

             Summary: camel-core - an encoded & (%26) in an endpoint URI option 
value splits the option: URISupport decodes the query before splitting it
                 Key: CAMEL-25453
                 URL: https://issues.apache.org/jira/browse/CAMEL-25453
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank


{{URISupport}} decodes the query of an endpoint URI before it splits it into 
options, so an encoded & ({{%26}}) in a value separates two options:

||URI||normalized (endpoint key)||parameters||
|{{log:foo?marker=Tom%26Jerry}}|{{log://foo?Jerry=&marker=Tom}}|{{marker=Tom}}, 
{{Jerry=}}|
|{{http://host/p?q=a%26b}}|{{http://host/p?b=&q=a}}|{{q=a}}, {{b=}}|
|{{log:foo?marker=%E2%82%AC%26}}|{{URISyntaxException}}: Trailing & marker 
found| |

Both {{URISupport.parseParameters(URI)}}, which {{DefaultComponent}} uses to 
configure the endpoint, and the complex normalizer in {{normalizeUri}} take the 
query from {{prepareQuery(URI)}}, which is {{URI.getQuery()}} (or the decoded 
scheme-specific part): every escape is already decoded when the {{URIScanner}} 
splits at {{&}}. The scanner itself does not decode values (it escapes {{%}} 
before {{URLDecoder}}), so an encoded {{&}} cannot survive.

What users see on main: a component that is not lenient fails to create the 
endpoint ({{log:foo?marker=Tom%26Jerry}}: _There are 1 parameters that couldn't 
be set on the endpoint ... Unknown parameters=[{Jerry=}]_). A lenient component 
such as {{http}} gets the value cut at the {{%26}} plus an extra empty 
parameter.

*The Endpoint DSL hits this too.* {{AbstractEndpointBuilder.computeUri}} 
encodes option values with {{URISupport.createQueryString}}, which writes {{&}} 
as {{%26}}, and resolves the result as an already normalized URI. So 
{{log("foo").marker("Tom&Jerry")}} builds {{log://foo?marker=Tom%26Jerry}} and 
fails to resolve on main in the same way. CAMEL-24253 fixed the same double 
decoding for {{+}} and {{%}} in Endpoint DSL values by wrapping them in 
{{RAW()}} (a regression of CAMEL-22293 in 4.14.0). {{&}} was not included.

The documented way is {{RAW(Tom&Jerry)}}, which works. But percent-encoding the 
value is what a URL builder produces and what the Endpoint DSL produces itself.

h3. Reproduction

On main (origin/main 374c04877418):
* New {{URISupportTest}} cases: {{normalizeUri("log:foo?marker=a%26b")}} gives 
{{log://foo?b=&marker=a}}, and {{parseParameters(new 
URI("log:foo?marker=a%26b&showAll=true"))}} gives {{marker=a}} plus {{b=}}.
* New {{EndpointUriEncodedAmpersandTest}} (camel-core): 
{{context.getEndpoint("log:foo?marker=Tom%26Jerry")}} throws 
{{ResolveEndpointFailedException}}.
* Both fail on main in 2 runs. A small program with the Endpoint DSL 
({{log("foo").marker("Tom&Jerry").resolve(context)}}) fails with main's 
camel-util, and gets {{Tom&Jerry}} with the fix.

h3. Proposed fix

Split before decoding the {{&}}: {{parseParameters}} and the complex normalizer 
decode the raw query like {{URI.getQuery()}}, except that {{%26}} becomes a 
private marker character. They parse it with the same {{parseQuery}} as before, 
then turn the marker back into {{&}} in the keys and values. A query without 
{{%26}} takes exactly the old code path. All other escapes, {{+}}, {{RAW(...)}} 
values and the double decoding of {{%2B}} that CAMEL-25190 leaves for Camel 5 
behave as before. {{prepareQuery(URI)}} (public) is unchanged.

A differential run over 300000 generated endpoint URIs, main vs fix, compared 
{{normalizeUri}}, {{parseParameters}} of the URI and {{parseParameters}} of the 
normalized URI. 233864 were the same. Every one that differs has {{%26}} in the 
URI or in its normalized form. Three of them are malformed {{RAW(}} values 
without the closing bracket, where the normalizer already made the {{&}} part 
of the value.

A Lean 4 model ({{proofs/CamelLean/R18EncodedAmpersand.lean}}) states the query 
as literal characters and percent escapes. {{cex_main_splits_encoded_amp}} 
computes main's two parameters for {{marker=a%26b}}. {{fix_eq_spec}} proves 
that for every query the fix splits only at a literal {{&}} (and otherwise 
decodes and splits key and value as main). {{fix_eq_main_without_encoded_amp}} 
proves that for every query without {{%26}} the fix gives main's result. RAW 
values and the decoding the scanner does after splitting are outside the model.

Not in scope: the un-encoded form the Endpoint DSL builds for {{toD}} 
({{getRawUri()}}: {{log://foo?marker=Tom&Jerry}}). That can only be fixed in 
the DSL, by adding {{&}} to the characters that 
{{AbstractEndpointBuilder.wrapRawIfNeeded}} wraps in {{RAW()}} (as CAMEL-24253 
did for {{+}} and {{%}}).

Affected: main and the 4.x releases. {{prepareQuery}} is the same in 4.14.0, 
4.18.3 and 4.22.1. For the Endpoint DSL since 4.14.0 (CAMEL-22293).

Duplicate check (2026-10-06): JIRA text "normalizeUri" (21 issues, among them 
CAMEL-25345, CAMEL-25190, CAMEL-25188, CAMEL-24187), "ampersand" (CAMEL-6977 
from 2013: {{authPassword=4%26Xy%25}} failed, answered with a pointer to RAW), 
Endpoint DSL encoding (CAMEL-24253, CAMEL-15015). None covers {{%26}}. GitHub 
pull requests: none.

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to