Andrea Cosentino created CAMEL-25345:
----------------------------------------
Summary: camel-util - URISupport.normalizeUri is not idempotent
when the path has # or two @ and a query value has = or #
Key: CAMEL-25345
URL: https://issues.apache.org/jira/browse/CAMEL-25345
Project: Camel
Issue Type: Bug
Components: camel-core
Reporter: Andrea Cosentino
CAMEL-25188 made the *query* side of {{URISupport.normalizeUri}} idempotent.
The *path* side still changes on a second pass, for two triggers only.
h2. Reproduction
Against {{camel-util}} built from current main:
{noformat}
[NOT IDEMPOTENT] sftp://[email protected]@host/in?password=pa=ss
pass1: sftp://[email protected]@host/in?password=pa%3Dss
pass2: sftp://me%40example.com@host/in?password=pa%3Dss
[NOT IDEMPOTENT] sql:select+*+from+t+where+id=:#id?dataSource=#ds
pass1: sql://select+*+from+t+where+id=:#id?dataSource=%23ds
pass2: sql://select+*+from+t+where+id=:%23id?dataSource=%23ds
[NOT IDEMPOTENT]
imaps://[email protected]@imap.example.com?password=x&sslContextParameters=#ssl
pass1:
imaps://[email protected]@imap.example.com?password=x&sslContextParameters=%23ssl
pass2:
imaps://me%[email protected]?password=x&sslContextParameters=%23ssl
[idempotent] direct:start?foo=bar
{noformat}
h2. Why
{{normalizeUri}} dispatches on {{CamelURIParser.fastParseUri}}:
{code:java}
String[] parts = CamelURIParser.fastParseUri(uri);
if (parts != null) {
...
return doFastNormalizeUri(parts);
} else {
return doComplexNormalizeUri(uri);
}
{code}
The fast path copies the path through verbatim. The first pass encodes the
*query* ({{=}} to {{%3D}}, {{#}} to {{%23}}), and that introduced {{%}} is what
makes the second pass take the complex branch - which then also encodes the
path, turning {{#}} into {{%23}} and the second {{@}} into {{%40}}.
So both triggers need a query value containing {{=}} or {{#}} (a {{#bean}}
reference is enough) *and* a path containing {{#}} or a second {{@}} (an email
address as the user, which is ordinary for sftp/imaps).
h2. End to end impact
Smaller than the normalization difference suggests, and only one case is a
regression:
* The *two-{{@}}* case is a regression against 4.22.1:
{{getEndpoint(endpoint.getEndpointUri())}} normalizes a second time, does not
match the cached key, and creates a **duplicate endpoint**; {{hasEndpoint}}
returns null for it.
* The *{{#}}-in-path* case was already missed on 4.22.1, because
{{getEndpointUri()}} returns the {{UnsafeUriCharactersEncoder}} form.
* {{interceptSendToEndpoint}} with the exact URI matches on both.
h2. Fix direction (validated, not yet implemented)
In {{normalizeUri}}, when {{doFastNormalizeUri(parts)}} produced a {{%}},
return {{doComplexNormalizeUri(uri)}} instead, so one URI never gets normalized
by two different normalizers.
A 600k-URI fuzz over three seeds, comparing against 4.22.1, reported 0
non-idempotent results and no change to the values a default component
receives, with all 76 {{URISupportTest}} cases passing.
**That evidence comes from an earlier run and has not been reproduced since**,
and this is a central code path, so it is worth re-running before the change
lands. Note also the precedent in CAMEL-25190, where a related normalization
defect was deliberately deferred to Camel 5 because fixing it would change the
values endpoints receive. The difference here is that this fix is intended to
be value-preserving and to affect only second-pass stability - which is exactly
the claim the fuzz needs to confirm.
Filed unassigned.
h2. Doc nit
The CAMEL-24524 section of {{camel-4x-upgrade-guide-4_23.adoc}} still ends by
describing the "(now consistently) unencoded form", which no longer matches the
behaviour after CAMEL-25188.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)