Yesterday, I was working with the Craft team at InVision to try and debug a rather odd URL-encoding problem. It seems that the
CFLocation tag double-encodes the forward-slash (
/); but, only when certain combinations of characters are present in the query-string parameter. I've been able to create an isolated example of this in Lucee CFML 22.214.171.124.
We were working on a ColdFusion page that accepted a Craft Client value in the URL. The value looks like this:
In the context of a URL's query-string, this value contains two "special" characters: the forward-slash (
/) and the plus (
+). So, when passing this value through to another page, via
CFLocation, I was sure to use the
encodeForUrl() security function. However, the incoming value seemed to have a somewhat incomplete encoding of its own, which I think is - at least in part - what was causing the issue. That said, here's my reproduction in Lucee CFML:
<cfscript> // The value we are passing-through the URL has two "special" characters in it: the // forward-slash ("/") and the plus ("+"). Let's see what happens when we pass- // through different combinations of encodings on these two characters. // -- // NOTE: In URL parlance, the "+" typically represents a Space character. targetUrl = ( "./test.cfm" & "?value=CraftManager/1.0.97+196" & "&value=CraftManager%2F1.0.97+196" & "&value=CraftManager%2F1.0.97%2B196" & "&value=#encodeForUrl( 'CraftManager/1.0.97+196' )#" & "&value=#urlEncodedFormat( 'CraftManager/1.0.97+196' )#" ); // Let's included the generated URL as an HTTP Header as well, so we can see how the // value differs from the value in the "Location" header. header name = "X-Target-Url" value = targetUrl ; location url = targetUrl addToken = false ; </cfscript>
As you can see, we're passing a number of
value parameters through to the next page using Lucee's URL-Array notation. Each value uses a different combination of encoded and unencoded values. In addition to the
Location header that ColdFusion is automatically including, I'm also explicitly adding an
X-Target-Url HTTP header so we can spot any discrepancies.
NOTE: This issue happens with or without the special Array-notation. I'm just using it in this case because it makes the subsequent
The target page just dumps-out the
<cfdump label="Url Scope (Lucee CFML #server.lucee.version#)" var="#url#" /> <p> <a href="./">Try again</a> </p>
So now, if we run the index-page and get redirect, we get the following browser output:
As you can see, the second instance of our
value query-string parameter contains an encoded forward-slash. Which means, it was sent through to the target page as a double-encoded slash. Specifically, it was this version:
This version contains an encoded forward-slash and an unencoded plus. Something about this combination of characters makes for an unhappy
If we look at the HTTP headers during the relocation, we can spot the discrepancy:
As you can see, when the
CFLocation tag goes to encode the plus, it appears to double-encode the already-encoded forward-slash.
To work around this, I just replaced all instances of the encoded slash in the URL prior to calling the
.replaceNoCase( "%2F", "/", "all" )
This fixes the issue in this use-case, at least temporarily.
I tried digging around in the Lucee CFML source-code to figure out what was going on; but, I couldn't pin-point the issue:
So, ultimately, my problem is likely that the original value,
CraftManager/1.0.97+196, was not encoded correctly in the call to the Lucee CFML server - I think the
+ wasn't being encoded. That said, something is clearly fishy in the
While not directly related to this, it appears that someone else posted a JIRA ticket about the auto-encoding creating unexpected value: