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) <noreply@anthropic.com>
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]
+