4a642d5f4c2a3102d5c2620af754d876146d7c24
max
  Sat Sep 26 21:53:49 2026 -0700
trackHub: restrict a hub genome's organism/description to a plain display-label character set

diff --git src/hg/lib/trackHub.c src/hg/lib/trackHub.c
index 64f02b61594..4c461193435 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -525,30 +525,42 @@
 return str;
 }
 
 static void checkHubIdName(char *type, char *name)
 /* Abort if name holds a character that is not valid in an identifier.  A hub
  * identifier - a genome name, a group name - is printed into dozens of URLs,
  * form values and attributes all over the CGIs, so the check belongs here rather than at every
  * one of those places.  Real names use letters, digits, underscore, dot and dash. */
 {
 if (strchr(name, '<') || strchr(name, '>') || strchr(name, '"')
     || strchr(name, '\'') || strchr(name, '&'))
     errAbort("Bad %s name: \"%s\". The characters < > \" ' and & are not allowed in a %s name.",
              type, name, type);
 }
 
+static void checkHubDisplayText(char *type, char *name)
+/* Abort if name holds a character that is not valid in a short display label (a hub's
+ * organism name or freeze/date label).  These get substituted into all sorts of contexts
+ * in the CGIs, including attribute values, so keep the same restriction as checkHubIdName
+ * plus the punctuation an attribute value has no legitimate use for. Real labels use
+ * letters, digits, spaces and ordinary punctuation like . , - / ( ). */
+{
+if (strpbrk(name, "<>\"'&:;") || strchr(name, '\n') || strchr(name, '\r'))
+    errAbort("Bad %s: \"%s\". The characters < > \" ' & : ; and newlines are not allowed in a %s.",
+             type, name, type);
+}
+
 struct grp *readGroupRa(char *groupFileName)
 /* Read in the ra file that describes the groups in an assembly hub. */
 {
 if (groupFileName == NULL)
     return NULL;
 struct hash *ra;
 struct grp *list = NULL;
 struct lineFile *lf = udcWrapShortLineFile(groupFileName, NULL, MAX_HUB_GROUP_FILE_SIZE);
 while ((ra = raNextRecord(lf)) != NULL)
     {
     char *str;
     struct grp *grp;
     AllocVar(grp);
     slAddHead(&list, grp);
 
@@ -727,34 +739,37 @@
     AllocVar(el);
     el->name = cloneString(genome);
     el->trackDbFile = trackHubRelativeUrl(url, trackDb);
     el->trackHub = hub;
     hashAdd(hash, el->name, el);
     hashAdd(hash, hubConnectSkipHubPrefix(el->name), el);
     slAddHead(&list, el);
     char *orderKey = hashFindVal(ra, "orderKey");
     if (orderKey != NULL)
 	el->orderKey = sqlUnsigned(orderKey);
 
     char *groups = hashFindVal(ra, "groups");
     if (twoBitPath != NULL)
 	{
 	el->description  = hashFindVal(ra, "description");
+	if (el->description != NULL)
+	    checkHubDisplayText("genome description", el->description);
 	char *organism = hashFindVal(ra, "organism");
 	if (organism == NULL)
 	    errAbort("must have 'organism' set in assembly hub in stanza ending line %d of %s",
 		     lf->lineIx, lf->fileName);
+	checkHubDisplayText("organism", organism);
 	el->organism  = addHubName(organism, hub->name);
 	hashReplace(ra, "organism", el->organism);
 	el->defaultPos  = hashFindVal(ra, "defaultPos");
 	if (el->defaultPos == NULL)
 	    errAbort("must have 'defaultPos' set in assembly hub in stanza ending line %d of %s",
 		     lf->lineIx, lf->fileName);
 	el->twoBitPath = trackHubRelativeUrl(url, twoBitPath);
         if (twoBitBptUrl != NULL)
             el->twoBitBptUrl = trackHubRelativeUrl(url, twoBitBptUrl);
 
 	char *htmlPath = hashFindVal(ra, "htmlPath");
 	if (htmlPath != NULL)
 	    hashReplace(ra, "htmlPath",trackHubRelativeUrl(url, htmlPath));
 	if (groups != NULL)
 	    el->groups = trackHubRelativeUrl(url, groups);