e6b0dc7877ae4b24f56682885e03263fd2da3582
braney
  Tue Aug 18 11:37:04 2026 -0700
lib, hgc: encode barChart, BAM and VCF detail text consistently, refs #38123

diff --git src/hg/lib/barChartUi.c src/hg/lib/barChartUi.c
index 3bcbecf4071..70410a14e4c 100644
--- src/hg/lib/barChartUi.c
+++ src/hg/lib/barChartUi.c
@@ -1,90 +1,93 @@
 /* Bar chart track controls */
 
 /* Copyright (C) 2015 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "cheapcgi.h"
+#include "htmshell.h"
 #include "cart.h"
 #include "net.h"
 #include "errCatch.h"
 #include "web.h"
 #include "hui.h"
 #include "trackDb.h"
 #include "jsHelper.h"
 #include "hCommon.h"
 #include "rainbow.h"
 #include "htmlColor.h"
 #include "barChartCategory.h"
 #include "barChartUi.h"
 #include "facetedTable.h"
 #include "tablesTables.h"
 
 /* Restrict features on right-click (popup) version */
 static boolean isPopup = FALSE;
 
 /* Convenience functions for category filter controls */
 
 
 char *makeCategoryLabel(struct barChartCategory *categ)
 /* Display category color and label */
 {
-char buf[256];
-safef(buf, sizeof(buf), "<td class='bcColorPatch' bgcolor=#%06x></td>"
+// the label comes from the barChartBars setting, which a track hub controls, escape it.
+// A dyString rather than a fixed buffer, since escaping can grow a long label past 256 bytes.
+struct dyString *dy = dyStringNew(256);
+dyStringPrintf(dy, "<td class='bcColorPatch' bgcolor=#%06x></td>"
                    "<td>&nbsp;%s</td>", 
-                                categ->color, categ->label);
-return(cloneString(buf));
+                                categ->color, htmlEncode(categ->label));
+return(dyStringCannibalize(&dy));
 }
 
 struct categorySelect
     {
     struct categorySelect *next;
     char *name;
     char *label;
     boolean checked;
     };
 
 static void makeGroupCheckboxes(char *name, char *title, struct categorySelect *selects)
 {
 #define TABLE_COLUMNS 1
 if (title != NULL)
-    printf("<tr><td colspan=10><i><b>%s</b></i></td></tr><tr>\n", title);
+    printf("<tr><td colspan=10><i><b>%s</b></i></td></tr><tr>\n", htmlEncode(title));
 int count = slCount(selects);
 struct categorySelect **categArray;
 AllocArray(categArray, count);
 int i=0;
 struct categorySelect *sel;
 for (i=0, sel = selects; sel != NULL; sel = sel->next, i++)
     categArray[i] = sel;
 int col=0;
 int row=0;
 int tableColumns=1;
 for (i=0; i<count; i++)
     {
     int j = row + col*(count/tableColumns+1);
     if (j>=count)
         {
         printf("</tr><tr>");
         row++;
         col = 0;
         }
     j = row + col*(count/tableColumns+1);
     if (!isPopup)
         {
         printf("<td><input type=checkbox name=\"%s\" value=\"%s\" %s></td>" "<td>%s</td>\n",
-                name, categArray[j]->name, categArray[j]->checked ? "checked" : "", 
-                categArray[j]->label);
+                name, htmlEncode(categArray[j]->name), categArray[j]->checked ? "checked" : "", 
+                categArray[j]->label);   // label is escaped html already, from makeCategoryLabel
         }
     col++;
     }
 if ((i % tableColumns) != 0)
     while ((i++ % tableColumns) != 0)
         printf("<td></td>");
 printf("</tr><tr><td></td></tr>\n");
 }
 
 static void makeCategoryCheckboxes(char *name, struct barChartCategory *categs, 
                                         struct slName *checked)
 {
 puts("<style>\n");
 puts(".bcColorPatch { padding: 0 10px; }\n");
 puts("</style>\n");
@@ -167,31 +170,32 @@
 }
 
 void barChartUiViewLimits(struct cart *cart, char *track, struct trackDb *tdb)
 /* Set viewing limits if log transform not checked */
 /* NOTE: this code from gtexUi.c.  Consider sharing. */
 {
 char buf[512];
 boolean isLogTransform = barChartIsLogTransformed(cart, track, tdb);
 safef(buf, sizeof buf, "%sViewLimitsMaxLabel %s", track, isLogTransform ? "disabled" : "");
 printf("<span class='%s'><b>View limits maximum:</b></span>\n", buf);
 double viewMax = barChartCurViewMax(cart, track, tdb);
 char cartVar[1024];
 safef(cartVar, sizeof(cartVar), "%s.%s", track, BAR_CHART_MAX_VIEW_LIMIT);
 cgiMakeDoubleVarWithExtra(cartVar, viewMax, 4, isLogTransform ? "disabled" : "");
 char *unit = trackDbSettingClosestToHomeOrDefault(tdb, BAR_CHART_UNIT, "");
-printf("<span class='%s'> %s (range 0-%d)</span>\n", buf, unit, 
+// barChartUnit comes from trackDb, which a track hub controls, escape it
+printf("<span class='%s'> %s (range 0-%d)</span>\n", buf, htmlEncode(unit), 
                                 round(barChartUiMaxMedianScore(tdb)));
 }
 
 // TODO: libify
 static boolean isUrl(char *url)
 {
 return startsWith("http://", url)
    || startsWith("https://", url)
    || startsWith("ftp://", url);
 }
 
 static void getCategsFromSettings(char *track, char *labelSetting, char *colorSetting, 
                                         struct slName **retLabels, struct slName **colors)
 /* Get category labels and optionally colors, from track settings */
 {
@@ -400,31 +404,31 @@
 
 puts("<div>");
 barChartUiLogTransform(cart, track, tdb);
 
 /* Viewing limits max.  This control is disabled if log transform is selected */
 // construct class so JS can toggle
 puts("&nbsp;&nbsp;");
 barChartUiViewLimits(cart, track, tdb);
 puts("</div>");
 /* Category filter */
 printf("<br>");
 char *categoryLabel =  trackDbSettingClosestToHomeOrDefault(tdb, 
                     BAR_CHART_CATEGORY_LABEL, BAR_CHART_CATEGORY_LABEL_DEFAULT);
 char *db = cartString(cart, "db");
 struct barChartCategory *categs = barChartUiGetCategories(db, tdb, NULL);
-printf("<div><b>%s:</b>\n", categoryLabel);
+printf("<div><b>%s:</b>\n", htmlEncode(categoryLabel));  // barChartLabel is hub supplied
 char cartVar[1024];
 safef(cartVar, sizeof(cartVar), "%s.%s", track, BAR_CHART_CATEGORY_SELECT);
 if (isPopup)
     {
     printf("<a href='%s?db=%s&g=%s'><button type='button'>Change</button><a>",
                 hTrackUiForTrack(track), database, track);
     }
 else
     {
     jsMakeCheckboxGroupSetClearButton(cartVar, TRUE);
     puts("&nbsp;");
     jsMakeCheckboxGroupSetClearButton(cartVar, FALSE);
     }
 printf("</div>");
 struct slName *selectedValues = NULL;
@@ -444,31 +448,31 @@
 }
 
 void barChartFacetedUi(char *database, struct cart *cart, struct trackDb *tdb, char *track, 
                         char *title, boolean boxed)
 /* Bar chart track type that has an associated facets tables */
 {
 puts("<br>");
 barChartUiLogTransform(cart, track, tdb);
 puts("&nbsp;&nbsp;");
 barChartUiViewLimits(cart, track, tdb);
 
 if (isPopup)
     {
     char *categoryLabel =  trackDbSettingClosestToHomeOrDefault(tdb,
                     BAR_CHART_CATEGORY_LABEL, BAR_CHART_CATEGORY_LABEL_DEFAULT);
-    printf("<div><b>%s:</b>\n", categoryLabel);
+    printf("<div><b>%s:</b>\n", htmlEncode(categoryLabel));  // barChartLabel is hub supplied
     printf("<a href='%s?db=%s&g=%s'><button type='button'>Change</button><a>",
                 hTrackUiForTrack(track), database, track);
     }
 else
     {
     char *facets = trackDbSetting(tdb, "barChartFacets");
     char *statsFile = hReplaceGbdb(trackDbRequiredSetting(tdb, "barChartStatsUrl"));
 
     /* Write html to make white background */
     hInsideStyleToWhite();
 
     /* Set up url that has enough context to get back to us.  */
     struct dyString *returnUrl = dyStringNew(0);
     dyStringPrintf(returnUrl, "../cgi-bin/hgTrackUi?db=%s&g=%s", database, track);
     dyStringPrintf(returnUrl, "&%s", cartSidUrlString(cart));