[
https://issues.apache.org/jira/browse/CODEC-345?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18117494#comment-18117494
]
Gary D. Gregory commented on CODEC-345:
---------------------------------------
Fixed in git master and snapshot builds in
https://repository.apache.org/content/repositories/snapshots/commons-codec/commons-codec/1.23.0-SNAPSHOT
> URLCodec.encodeUrl(BitSet, byte[]) ignores the caller-supplied BitSet for %
> and +
> ---------------------------------------------------------------------------------
>
> Key: CODEC-345
> URL: https://issues.apache.org/jira/browse/CODEC-345
> Project: Commons Codec
> Issue Type: Bug
> Affects Versions: 1.22.1
> Reporter: Ganesh Gautam
> Assignee: Gary D. Gregory
> Priority: Major
> Fix For: 1.23.0
>
>
> {{URLCodec.encodeUrl(BitSet urlsafe, byte[] bytes)}} is public API and is
> widely used as a general-purpose percent-encoder, with the caller passing the
> character set appropriate to the URI component being encoded. The javadoc
> describes the parameter as {_}"bitset of characters deemed URL safe"{_}.
> CODEC-339 changed the method to percent-escape {{%}} and {{+}}
> unconditionally, even when the caller has explicitly marked them safe:
> {code:java}
> - if (urlsafe.get(b)) {
> + if (urlsafe.get(b) && b != ESCAPE_CHAR && b != PLUS_CHAR) {
> {code}
>
> For callers that use the {{www-form-urlencoded}} default set this is a no-op.
> For callers that supply their own set, which is the only reason the overload
> exists, it now silently changes the output:
> * a legal literal {{+}} in a URI path or query becomes {{{}%2B{}}}, changing
> the meaning of the URI;
> * an already-percent-encoded input is {*}double-encoded{*}, because {{%}}
> becomes {{{}%25{}}}. Encoders of this shape are routinely applied to whole
> URLs that may already contain valid escapes, so {{Caf%C3%A9}} becomes
> {{{}Caf%25C3%25A9{}}}.
> The same reasoning applies to {{{}PercentCodec{}}}, where the
> {{plusForSpace}} constructor now adds {{+}} to {{{}alwaysEncodeChars{}}}, so
> a literal {{+}} in the input is no longer round-trippable.
> We appreciate that the intent was to make {{encodeUrl}} or {{decodeUrl}}
> round-trip safely. Our point is narrower: that guarantee is only meaningful
> for the codec's _own_ {{WWW_FORM_URL}} alphabet, and enforcing it inside the
> BitSet overload removes the caller's ability to encode a URI component
> correctly.
> h3. Reproduction
> Self-contained and no dependency beyond commons-codec itself. Full source:
> {code:java}
> import java.util.BitSet;
> import org.apache.commons.codec.net.URLCodec;
> public class Repro {
> public static void main(String[] args) throws Exception {
> // RFC 2396 abs_path, i.e. what HtmlUnit's UrlUtils and HttpClient
> 3.x's URI build.
> BitSet allowed = new BitSet(256);
> for (int c = 'a'; c <= 'z'; c++) allowed.set(c);
> for (int c = 'A'; c <= 'Z'; c++) allowed.set(c);
> for (int c = '0'; c <= '9'; c++) allowed.set(c);
> for (char c : "-_.!~*'()".toCharArray()) allowed.set(c); // mark
> allowed.set('%'); // escaped
> for (char c : ":@&=+$,;/".toCharArray()) allowed.set(c); // pchar
> for (String s : new String[] {"/pages/1/Test+Page",
> "/display/TST/Caf%C3%A9"}) {
> System.out.println(s + " -> "
> + new String(URLCodec.encodeUrl(allowed,
> s.getBytes("UTF-8")), "US-ASCII"));
> }
> }
> }
> {code}
> ----
> {code:java}
> 1.22.0 (expected — the BitSet is honoured):
> ```
> /pages/1/Test+Page -> /pages/1/Test+Page
> /display/TST/Caf%C3%A9 -> /display/TST/Caf%C3%A9
> ```
> 1.22.1 (actual):
> ```
> /pages/1/Test+Page -> /pages/1/Test%2BPage
> /display/TST/Caf%C3%A9 -> /display/TST/Caf%25C3%25A9
> {code}
> ----
> h3. Downstream impact
> Two libraries were found that call this overload with their own BitSet:
> * *HtmlUnit* — {{org.htmlunit.util.UrlUtils}} /
> {{com.gargoylesoftware.htmlunit.util.UrlUtils}} (present in 2.15 through
> 3.11.0). {{UrlUtils.encode}} passes {{PATH_ALLOWED_CHARS}} /
> {{QUERY_ALLOWED_CHARS}} / {{{}ANCHOR_ALLOWED_CHARS{}}}, all of which include
> {{%}} and {{+}} by construction, and {{WebClient}} runs every request URL —
> including every redirect target through {{{}UrlUtils.encodeUrl{}}}.
> * *Commons HttpClient 3.x* — {{org.apache.commons.httpclient.URI}} and
> {{{}org.apache.commons.httpclient.util.URIUtil{}}}, same pattern.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)