c2a6ef817930149ad6f55818fa0196d99c47e02b braney Fri Sep 11 10:40:11 2026 -0700 cheapcgi: skip a CGI pair with no =value instead of aborting, refs #38335 Both query string parsers looked for the '=' across the whole rest of the string rather than inside the current pair. A pair with no '=' in it therefore ran into the pair after it and took its value. "g-catV2&db=hg38" was stored as one variable named "g-catV2&db", so db was lost with no warning, and that corrupt name was copied on into the cart. The same pair at the end of the string had no '=' left to find and aborted the whole request, which is what the "Mangled CGI input string g-catV2" entries in the hgw1 logs were. Both parsers now find the end of the pair first, keeping the existing separator precedence ('&', then ';' for DAS), and skip a pair with no '='. A mixed "a=1;b=2&c=3" still parses the way it did. Adds lib/tests/cgiParseTest, which runs 18 query strings through both parsers. It covers the empty pair of #38185 as well. Co-Authored-By: Claude Opus 5 (1M context) diff --git src/lib/tests/expected/cgiParseTest src/lib/tests/expected/cgiParseTest new file mode 100644 index 00000000000..2e38ca7d26e --- /dev/null +++ src/lib/tests/expected/cgiParseTest @@ -0,0 +1,72 @@ +in : db=hg38&position=chr1:1-1000 +next: [db=hg38][position=chr1:1-1000] +hash: [db=hg38][position=chr1:1-1000] + +in : a=1;b=2;c=3 +next: [a=1][b=2][c=3] +hash: [a=1][b=2][c=3] + +in : a=1;b=2&c=3 +next: [a=1;b=2][c=3] +hash: [a=1;b=2][c=3] + +in : &db=hg38&position=chr1:1-1000 +next: [db=hg38][position=chr1:1-1000] +hash: [db=hg38][position=chr1:1-1000] + +in : db=hg38&&position=chr1:1-1000 +next: [db=hg38][position=chr1:1-1000] +hash: [db=hg38][position=chr1:1-1000] + +in : db=hg38&position=chr1:1-1000&& +next: [db=hg38][position=chr1:1-1000] +hash: [db=hg38][position=chr1:1-1000] + +in : &&;&db=hg38 +next: [db=hg38] +hash: [db=hg38] + +in : g-catV2&db=hg38 +next: [db=hg38] +hash: [db=hg38] + +in : db=hg38&i&position=chr1:1-1000 +next: [db=hg38][position=chr1:1-1000] +hash: [db=hg38][position=chr1:1-1000] + +in : db=hg38&g-catV2 +next: [db=hg38] +hash: [db=hg38] + +in : g-catV2 +next: +hash: + +in : a=1&b=&c=3 +next: [a=1][b=][c=3] +hash: [a=1][b=][c=3] + +in : =v +next: [=v] +hash: [=v] + +in : a=1&=&b=2 +next: [a=1][=][b=2] +hash: [a=1][=][b=2] + +in : +next: +hash: + +in : & +next: +hash: + +in : hgt.customText=track name=one +next: [hgt.customText=track name=one] +hash: [hgt.customText=track name=one] + +in : position=chr1%3A1-1000&name=a+b&other=x%26y +next: [position=chr1:1-1000][name=a b][other=x&y] +hash: [position=chr1:1-1000][name=a b][other=x&y] +