983b7eff4b1c516c9cb7bb56cc1399216a127d51
max
  Wed Aug 19 04:02:39 2026 -0700
address v503 preview1 code review (#38141): escaping and oauth fixes

Fixes the six items Brian raised reviewing the XSS sweep (#38057) and the
BLAT-results group work (#38086):

- hgGenome/configure.c, hgPal.c: drop htmlEncode() on cartWebStart title args;
cartWebStart already escapes the title, so this was double-escaping.
- hgSession.c doReSaveSession: htmlEncode the user name and pass the encoded
name to getSessionLink (same fix already applied at line 1544).
- hgUserSuggestion.c printInvalidForm: cgiEncode the five cart values echoed
into the mailto: href (reachable on the robot/captcha path).
- hgSearch.c: cgiEncode db for the hgTracks URL query parameter instead of
reusing the JSON-escaped copy meant for the JS string literal.
- hgLogin.c oauthReturn: clone oauth_provider before cartRemove frees it, so
the later oauthFetchIdentity call is not a read-after-free.
- customFactory.c checkGroup: only accept group=blat when blatResultsGroup is
on, matching hgTracks; otherwise the group is never created and the track
would orphan into 'other'.

refs #38141, refs #38057, refs #38086

diff --git src/hg/hgSearch/hgSearch.c src/hg/hgSearch/hgSearch.c
index 726cce77400..5c28caec17e 100644
--- src/hg/hgSearch/hgSearch.c
+++ src/hg/hgSearch/hgSearch.c
@@ -606,31 +606,33 @@
     trackHubFixName(trackName);
     puts("Content-type:text/html\n");
     puts("<HTML>\n<HEAD>\n");
     printf("<script type='text/javascript' src='../js/utils.js'></script>\n");
     printf("<script>\n");
     // we are about to redirect back to hgTracks, save the search term onto the
     // history stack so it will appear in the dropdown of auto-suggestions before
     // redirecting.  db and userSearch are user-supplied and go into a JS string literal
     // inside this inline <script>; jsonStringEscape escapes quotes and '/' so neither the
     // string literal nor a literal </script> can break out (XSS).
     char *jsDb = jsonStringEscape(db);
     char *jsSearch = jsonStringEscape(userSearch);
     printf("addRecentSearch(\"%s\", \"%s\", {\"label\": \"%s\", \"value\": \"%s\", \"id\": \"%s\"});\n",
             jsDb, jsSearch, jsSearch, jsSearch, newPosBuf);
     printf("window.location.href=\"../cgi-bin/hgTracks?");
-    printf("db=%s", jsDb);
+    // db here is a URL query parameter, so cgi-encode it rather than reuse the JS-escaped jsDb
+    char *urlDb = cgiEncode(db);
+    printf("db=%s", urlDb);
     printf("&position=%s", newPosBuf);
     if (!sameString(trackName, "chromInfo"))
         printf("&%s=pack", trackName);
     printf("&hgFind.matches=%s", hgp->singlePos->name);
     if (track && track->parent)
         {
         if (tdbIsSuperTrackChild(track))
             printf("&%s=show", track->parent->track);
         else
             {
             // tdb is a subtrack of a composite or a view
             printf("&%s_sel=1&%s_sel=1", trackName, track->parent->track);
             }
         }
     printf("\"</script>\n");