007346754d9616c97219877c3731714eaf62c1dc chmalee Tue Jun 24 11:51:27 2025 -0700 When encoding characters in a URL, we only want to encode characters that have not already been encoded, refs #35925 diff --git src/hg/js/utils.js src/hg/js/utils.js index 35b0ffa8fbb..6492729e5ea 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -1165,37 +1165,42 @@ retVal += "&"; } var val = varHash[aVar]; if (typeof(val) === 'string' && val.length >= 2 && val.indexOf('[') === 0 && val.lastIndexOf(']') === (val.length - 1)) { var vals = val.substr(1,val.length - 2).split(','); /* jshint loopfunc: true */// function inside loop works and replacement is awkward. $(vals).each(function (ix) { if (ix > 0) retVal += "&"; retVal += aVar + "=" + encodeURIComponent(this); }); } else { - let decoded = decodeURIComponent(val); - if (decoded.length < val.length) { - // val was already encoded, don't re-encode - retVal += aVar + "=" + val; - } else { - retVal += aVar + "=" + encodeURIComponent(val); - } + // sometimes val is already encoded or partially encoded + // the CGI cannot know if user input is double encoded + // so test for already encoded characters here and only + // encode what we need to + retVal += aVar + "=" + val.replace(/(%[0-9A-Fa-f]{2})+|[^%]+/g, (match) => { + if (/%[0-9A-Fa-f]{2}/.test(match)) { + // Already percent-encoded, leave as-is + return match; + } + // Encode unencoded parts + return encodeURIComponent(match); + }); } } return retVal; } function getAllVarsAsUrlData(obj) // DEAD CODE? { // Returns a string in the form of var1=val1&var2=val2... for all inputs and selects in an obj // If obj is undefined then obj is document! return varHashToQueryString(getAllVars(obj)); } /* function popupBox(popit, content, popTitle) {