c4eb98684d52ed28bd00632854ca5c8385b1bb63
braney
  Tue Aug 18 11:24:43 2026 -0700
trackHub: tighten genome name validation, refs #38123

diff --git src/hg/lib/trackHub.c src/hg/lib/trackHub.c
index ba8f9aaf229..0ada6dd9206 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -685,30 +685,37 @@
         break;
 
     char *twoBitPath = hReplaceGbdb(hashFindVal(ra, "twoBitPath"));
     char *twoBitBptUrl = hReplaceGbdb(hashFindVal(ra, "twoBitBptUrl"));
     char *genome, *trackDb;
     if (twoBitPath != NULL)
 	genome = addHubName(hashFindVal(ra, "genome"), hub->name);
     else
         genome = hashFindVal(ra, "genome");
     if (hub->defaultDb == NULL)
 	hub->defaultDb = genome;
     if (genome == NULL)
         badGenomeStanza(lf);
     if (hasWhiteSpace(genome))
         errAbort("Bad genome name: \"%s\". Only alpha-numeric characters and \"_\" are allowed ([A-Za-z0-9_]).", genome);
+    // The genome name becomes the db name, which is printed into dozens of URLs and form
+    // values all over the CGIs.  Real assembly names never contain any of these characters,
+    // so reject them here.
+    if (strchr(genome, '<') || strchr(genome, '>') || strchr(genome, '"')
+        || strchr(genome, '\'') || strchr(genome, '&'))
+        errAbort("Bad genome name: \"%s\". The characters < > \" ' and & are not allowed in a "
+                 "genome name.", genome);
     if (hashLookup(hash, genome) != NULL)
         errAbort("Duplicate genome %s in stanza ending line %d of %s",
 		genome, lf->lineIx, lf->fileName);
     if (singleFile == NULL)
         {
         trackDb = hashFindVal(ra, "trackDb");
         if (trackDb == NULL)
             badGenomeStanza(lf);
         }
     else
         trackDb = singleFile;
     AllocVar(el);
     el->name = cloneString(genome);
     el->trackDbFile = trackHubRelativeUrl(url, trackDb);
     el->trackHub = hub;