9ce14583f270ff891278d2cf4f0c6432bf79fc61
braney
  Wed Sep 2 11:20:27 2026 -0700
hubConnect: hubEncode covers custom tracks as well as hub tracks, refs #38172

hubEncode() keyed off isHubTrack() alone, so the thirty-odd call sites that
use it treated a custom track's data fields as our own.  A custom track's
data comes from outside the same way a hub's does, and customFactory.c only
looks at the track and label lines, not the data fields, which are what
hubEncode() is called on.  Add isCustomTrack() to the test.

Raised in the v503 Preview II code review.

diff --git src/hg/lib/hubConnect.c src/hg/lib/hubConnect.c
index 7e6cf51c3c8..213b7b9ef79 100644
--- src/hg/lib/hubConnect.c
+++ src/hg/lib/hubConnect.c
@@ -17,61 +17,66 @@
 #include "net.h"
 #include "trackHub.h"
 #include "hubConnect.h"
 #include "hui.h"
 #include "errCatch.h"
 #include "obscure.h"
 #include "hgConfig.h"
 #include "grp.h"
 #include "udc.h"
 #include "hubPublic.h"
 #include "genark.h"
 #include "asmAlias.h"
 #include "cheapcgi.h"
 #include "htmshell.h"
 #include "quickLift.h"
+#include "customTrack.h"
 
 boolean hubsCanAddGroups()
 /* can track hubs have their own groups? */
 {
 static boolean canHubs = FALSE;
 static boolean canHubsSet = FALSE;
 
 if (!canHubsSet)
     {
     canHubs = cfgOptionBooleanDefault("trackHubsCanAddGroups", TRUE);
     canHubsSet = TRUE;
     }
 
 return canHubs;
 }
 
 boolean isHubTrack(char *trackName)
 /* Return TRUE if it's a hub track. */
 {
 return startsWith(hubTrackPrefix, trackName);
 }
 
 char *hubEncode(struct trackDb *tdb, char *text)
-/* Return text escaped for HTML if it belongs to a track hub, otherwise return it unchanged.
- * A hub's trackDb, autoSql schema and data file are all written by a stranger, so anything
- * from them has to be escaped before it goes in the page.  Our own tracks are a
+/* Return text escaped for HTML if it belongs to a track hub or to a custom track, otherwise
+ * return it unchanged.  A hub's trackDb, autoSql schema and data file are all written by a
+ * stranger, so anything from them has to be escaped before it goes in the page.  A custom
+ * track's data comes from outside too, and one person can drop a custom track into another
+ * person's session with a link that carries hgt.customText, so it gets the same treatment;
+ * customFactory.c strips JavaScript from the track and label lines but leaves the data
+ * fields alone, and the data fields are what this is called on.  Our own tracks are a
  * different case: they put real HTML in fields on purpose - ClinVar's review-status stars,
  * the CRISPR track's links in an extra column, the <BR> in the Denisova schema comments -
  * and escaping those would print the markup instead of rendering it. */
 {
-if (text != NULL && tdb != NULL && isHubTrack(tdb->track))
+if (text != NULL && tdb != NULL && (isHubTrack(tdb->track) || isCustomTrack(tdb->track)))
     return htmlEncode(text);
 return text;
 }
 
 static char *hubStatusTableName = NULL;
 static char *_hubPublicTableName = NULL;
 
 static char *getHubStatusTableName()
 /* return the hubStatus table name from the environment, 
  * or hg.conf, or use the default.  Cache the result */
 {
 if (hubStatusTableName == NULL)
     hubStatusTableName = cfgOptionEnvDefault("HGDB_HUB_STATUS_TABLE",
 	    hubStatusTableConfVariable, defaultHubStatusTableName);