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)