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/hVarSubst.c src/hg/lib/hVarSubst.c
index 39c95fdd367..c4cc5e51868 100644
--- src/hg/lib/hVarSubst.c
+++ src/hg/lib/hVarSubst.c
@@ -348,34 +348,35 @@
     if (*(next+1) == '$')
         {
         // $$ is a literal $
         dyStringAppendC(dest, '$');
         start = next = next + 2;
         }
     else if (htmlVars != NULL)
         {
         // variable reference, or just a dollar sign in the text
         char *after = parseVarNameMaybe(next, varName, sizeof(varName));
         if ((after != NULL) && isHtmlVar(tdb, varName, htmlVars, htmlVarCount))
             {
             /* Escape the value before it goes into the page.  A hub's description html was
              * sanitized once, when the hub was read (trackHub.c, htmlSanitize); this pass
              * runs at render time, long after, so anything it inserted raw would be markup
-             * that nothing had ever looked at.  Two of the variables are exactly that:
-             * $organism and $date come straight out of a hub's genomes.txt with no
-             * validation.  None of the variables in either list is meant to carry markup,
-             * so escaping them all costs nothing and leaves no gap to keep track of. */
+             * that nothing had ever looked at.  $organism and $date come straight out of a
+             * hub's genomes.txt, restricted at parse time (trackHub.c,
+             * checkHubDisplayText) to a safe display-label character set.  None of the
+             * variables in either list is meant to carry markup, so escaping them all here
+             * too costs nothing. */
             struct dyString *raw = dyStringNew(64);
             substVar(desc, tdb, database, varName, raw);
             char *escaped = htmlEncode(raw->string);
             dyStringAppend(dest, escaped);
             freeMem(escaped);
             dyStringFree(&raw);
             start = next = after;
             }
         else
             {
             dyStringAppendC(dest, '$');
             start = next = next + 1;
             }
         }
     else