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/lib/customFactory.c src/hg/lib/customFactory.c
index 5f01d3c78ed..6adb7b225c7 100644
--- src/hg/lib/customFactory.c
+++ src/hg/lib/customFactory.c
@@ -3779,32 +3779,34 @@
 }
 
 static void stripJavascript(char **pString)
 /* Replace *pString (which is dynamically allocated) with javascript free version of itself. */
 {
 char *tmp = *pString;
 *pString = jsStripJavascript(tmp);
 freeMem(tmp);
 }
 
 static boolean checkGroup(char *db, char *group)
 /* Check if group is valid in db (if mysql, in grp table; if hub, in groups file or default) */
 {
 // "blat" is the synthetic "BLAT Results" group that hgTracks adds in code (it is not a row in the
 // grp table), so accept it here; otherwise a BLAT result track's group=blat would be rejected and
-// forced back into the generic "user" (Custom Tracks) group.
-if (sameString(group, "blat"))
+// forced back into the generic "user" (Custom Tracks) group.  Only accept it when the feature is
+// on: with blatResultsGroup off hgTracks never creates the group, so a hand-written group=blat
+// would otherwise be an orphan that lands in "other".
+if (sameString(group, "blat") && cfgOptionBooleanDefault("blatResultsGroup", FALSE))
     return TRUE;
 static struct hash *dbToGroups = NULL;
 if (dbToGroups == NULL)
     dbToGroups = hashNew(0);
 struct hash *groups = hashFindVal(dbToGroups, db);
 if (groups == NULL)
     {
     groups = hashNew(0);
     hashAdd(dbToGroups, db, groups);
     struct grp *groupList = hLoadGrps(db), *grp;
     for (grp = groupList;  grp != NULL;  grp = grp->next)
         hashAddInt(groups, grp->name, TRUE);
     }
 return hashIntValDefault(groups, group, FALSE);
 }