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/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c
index 558997735e5..7fe4b2d549b 100644
--- src/hg/hgLogin/hgLogin.c
+++ src/hg/hgLogin/hgLogin.c
@@ -2314,31 +2314,32 @@
     {
     freez(&errMsg);
     errMsg = cloneString("Could not start social login. Please try again.");
     displayLoginPage(conn);
     return;
     }
 jsInlineF("window.location = '%s';\n", url);
 }
 
 void oauthReturn(struct sqlConnection *conn)
 /* Handle the provider's redirect back to us: verify state, exchange the code for the
  * user's identity, and resolve/auto-link the account. */
 {
 char *state = cgiUsualString("state", "");
 char *savedState = cartUsualString(cart, "oauth_state", "");
-char *provider = cartUsualString(cart, "oauth_provider", "");
+// clone this: cartRemove below frees the cart's copy, but we still use provider afterward
+char *provider = cloneString(cartUsualString(cart, "oauth_provider", ""));
 
 // Validate the anti-CSRF state before consuming any cart state or acting on an error param.  A
 // stray code/error link (a re-opened redirect, or a crafted hgLogin?error=...) must not be able to
 // consume the state nonce of a login in flight, so check first and only then clear the flow.  A
 // compliant provider echoes state on an error return too (RFC 6749 4.1.2.1), and we always send it.
 if (isEmpty(state) || isEmpty(savedState) || differentString(state, savedState))
     {
     freez(&errMsg);
     errMsg = cloneString("Your login session expired or was invalid. Please try again.");
     displayLoginPage(conn);
     return;
     }
 cartRemove(cart, "oauth_state");      // one-time use
 cartRemove(cart, "oauth_provider");   // end the flow so a later code/error param can't re-enter