20ca104b9897669e127e4ec8675321b12f780cff
braney
  Tue Aug 18 11:19:44 2026 -0700
lib, hgTracks: encode JSON object keys and configure/search text, refs #38123

diff --git src/hg/hgTracks/config.c src/hg/hgTracks/config.c
index fecdf3b03bf..52f5dcdd8bb 100644
--- src/hg/hgTracks/config.c
+++ src/hg/hgTracks/config.c
@@ -345,31 +345,32 @@
                             &indicator, &otherState);
     hPrintf("<TR NOWRAP class='nativeToggleBar'>");
     hPrintf("<TH NOWRAP align='left' colspan=3>");
     hPrintf("<table style='width:100%%;'><tr class='noData'><td style='text-align:left;'>");
     hPrintf("\n<A NAME='%sGroup'></A>",group->name);
     hPrintf("<input type=hidden name='%s' id='%s' value=%d>",
             collapseGroupVar(group->name),collapseGroupVar(group->name), (isOpen?0:1));
     char idText[256];
     safef(idText, sizeof idText, "%s_button", group->name);
     hPrintf("<IMG class='toggleButton' "
             "id='%s' src='%s' alt='%s' title='%s this group'>&nbsp;&nbsp;",
             idText, indicatorImg, indicator,isOpen?"Collapse":"Expand");
     // TODO XSS filter group->name
     jsOnEventByIdF("click", idText, "return vis.toggleForGroup(this,'%s');", group->name);
 
-    hPrintf("<B>&nbsp;%s</B> ", group->label);
+    // a hub group's label is built from the hub's shortLabel and its groups.txt label
+    hPrintf("<B>&nbsp;%s</B> ", htmlEncode(group->label));
     hPrintf("&nbsp;&nbsp;&nbsp;");
     hPrintf("</td><td style='text-align:right;'>\n");
     safef(idText, sizeof idText, "%s_hideAllBut", group->name);
     hPrintf("<INPUT TYPE=SUBMIT NAME=\"%s\" id='%s' VALUE=\"%s\" "
             "title='Hide all tracks in this group'>",
 	    configHideAll, idText, "Hide all");
     // TODO XSS filter configGroupTarget
     char jsText[256]; 
     // used several times
     safef(jsText, sizeof jsText, "document.mainForm.%s.value='%s'; %s",
 	    configGroupTarget, group->name, jsSetVerticalPosition("mainForm"));
     jsOnEventById("click", idText, jsText);
     hPrintf(" ");
     safef(idText, sizeof idText, "%s_showAllBut", group->name);
     hPrintf("<INPUT TYPE=SUBMIT NAME=\"%s\" id='%s' VALUE=\"%s\" "
@@ -461,36 +462,37 @@
     for (tr = group->trackList; tr != NULL; tr = tr->next)
         {
         struct track *track = tr->track;
         struct trackDb *tdb = track->tdb;
 
         hPrintf("<TR %sid='%s-%d'>",(isOpen ? "" : "style='display: none;'"),
                 group->name, rowCount++);
         hPrintf("<TD NOWRAP>");
         if (tdbIsSuperTrackChild(tdb))
             /* indent members of a supertrack */
             hPrintf("&nbsp;&nbsp;&nbsp;&nbsp;");
 
         hPrintIcons(tdb);
 
         if (track->hasUi)
+            // the labels come from trackDb, which a track hub controls, escape them
             hPrintf("<A TITLE='%s%s...' HREF='%s?%s=%s&db=%s&g=%s&hgTracksConfigPage=configure'>",
                     tdb->parent ? "Part of super track: " : "Configure ",
-                    tdb->parent ? tdb->parent->shortLabel : tdb->shortLabel,
+                    htmlEncode(tdb->parent ? tdb->parent->shortLabel : tdb->shortLabel),
                     hTrackUiForTrack(tdb->track),
                     cartSessionVarName(), cartSessionId(cart), database, track->track);
-        hPrintf(" %s", tdb->shortLabel);
+        hPrintf(" %s", htmlEncode(tdb->shortLabel));
         if (track->hasUi)
 	    hPrintf("</A>");
 	hPrintf("</TD><TD NOWRAP>");
         if (tdbIsSuperTrackChild(tdb))
             /* indent members of a supertrack */
             hPrintf("&nbsp;&nbsp;&nbsp;&nbsp;");
 
 	/* If track is not on this chrom print an informational
 	   message for the user. */
         if (tdbIsDownloadsOnly(tdb))    // No vis display for downloadsOnly
             hPrintf("<A TITLE='Downloadable files...' HREF='%s?%s=%s&g=%s'>Downloads</A>",
                     hgFileUiName(),cartSessionVarName(), cartSessionId(cart), tdb->track);
         else if (hTrackOnChrom(track->tdb, chromName))
 	    {
             if (tdbIsSuper(track->tdb))
@@ -499,31 +501,31 @@
                 superTrackDropDown(cart, track->tdb, 1);
                 }
             else
                 {
                 /* check for option of limiting visibility to one mode */
                 hTvDropDownClassVisOnlyWithLabel(track->track, track->visibility,
                                         rTdbTreeCanPack(track->tdb),
                                         (track->visibility == tvHide) ? "hiddenText trackVis" : "normalText trackVis",
                                         trackDbSetting(track->tdb, "onlyVisibility"),
                                         tdb->shortLabel);
                 }
 	    }
         else
 	    hPrintf("[No data-%s]", chromName);
 	hPrintf("</TD><TD NOWRAP>");
-        hPrintf("%s", tdb->longLabel);
+        hPrintf("%s", htmlEncode(tdb->longLabel));
 	hPrintf("</TD></TR>\n");
 	}
     hPrintf("<tr class='noData'><td colspan=3>");
     cgiDown(0.9);
     hPrintf("</td></tr>\n");
     }
 hashFree(&superHash);
 hPrintf("</TABLE>\n");
 
 jsInline("$(document).ready( cfgPageAddListeners )");
 }
 
 static int addDownloadOnlyTracks(char *db,struct group **pGroupList,struct track **pTrackList)
 // Download only tracks are not normaly incorporated into the grou and track lists
 {