e4c2e97b5eb35b8f9b7b71eb13a4c24e6c1ece22
max
  Mon Sep 14 09:19:02 2026 -0700
hui, hgTrackUi: encode trackDb setting and cart text in HTML attributes, refs #38123

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 33024787b93..593ca22fca2 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -544,31 +544,32 @@
 
 static void printDownloadUrl(char *downloadUrl, char *database, char *track)
 /* given a string <label><space><url>, print a nice download link */
 {
 char *parts[2];
 int partCount = chopByWhiteRespectDoubleQuotes(downloadUrl, parts, 2);
 if (partCount!=2)
     {
     puts("<b>Internal Error:</b> The downloadUrl trackDb statement needs exactly two arguments, the file type and the URL.");
     return;
     }
 char* fileType = parts[0];
 stripString(fileType, "\"");  // Remove double quotes, weird that chopByWhiteRespectDoubleQuotes doesn't do this
 char* url = parts[1];
 char *newUrl = replaceInUrl(url, "", NULL, database, "", 0, 0, track, FALSE, NULL);
-printf("<br>Download: <a href=\"%s\">%s</a>", newUrl, fileType);
+// downloadUrl may be hub supplied
+printf("<br>Download: <a href=\"%s\">%s</a>", htmlEncode(newUrl), htmlEncode(fileType));
 }
 
 static void makeFileDownloads(struct trackDb *tdb, char *db) 
 /* given either downloadUrl or downloadUrl.1/.2/... in trackDb, print links to these files.
  * File can be anywhere on the internet, useful e.g. for GTF files for gene tracks */
 {
 char *downloadUrl = trackDbSetting(tdb, "downloadUrl");
 struct slName* tdbNames = trackDbSettingsWildMatch(tdb, "downloadUrl.*");
 if (downloadUrl)
     printDownloadUrl(downloadUrl, db, tdb->track);
 
 while (tdbNames != NULL)
     {
     struct slName *tdbName = slPopHead(&tdbNames);
     printDownloadUrl(trackDbSetting(tdb, tdbName->name), db, tdb->track);
@@ -4349,43 +4350,43 @@
 if (filterBy->useIndex)
     {
     safef(varName, sizeof(varName), "%d",ix);
     name = varName;
     label = slValue->name;
     }
 else
     {
     label = (filterBy->valueAndLabel? slValue->name + strlen(slValue->name)+1: slValue->name);
     name = slValue->name;
     }
 printf("<OPTION");
 if (filterBy->slChoices != NULL && slNameInList(filterBy->slChoices,name))
     printf(" SELECTED");
 if (filterBy->useIndex || filterBy->valueAndLabel)
-    printf(" value='%s'",name);
+    printf(" value='%s'",htmlEncode(name));    // filterValues are hub supplied
 if (filterBy->styleFollows)
     {
     char *styler = label + strlen(label)+1;
     if (*styler != '\0')
 	{
 	if (*styler == '#') // Legacy: just the color that follows
-	    printf(" style='color: %s;'",styler);
+	    printf(" style='color: %s;'",htmlEncode(styler));
 	else
-	    printf(" style='%s'",styler);
+	    printf(" style='%s'",htmlEncode(styler));
 	}
     }
-printf(">%s</OPTION>\n",label);
+printf(">%s</OPTION>\n",htmlEncode(label));   // filterValues are hub supplied
 }
 
 static boolean filterByColumnIsMultiple(struct cart *cart, struct trackDb *tdb,  char *setting)
 /* Is this filter setting expecting multiple items (e.g. has checkboxes in the UI) */
 {
 return (sameString(setting, FILTERBY_MULTIPLE) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_OR) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_OR) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_ONLY_AND) ||
         sameString(setting, FILTERBY_MULTIPLE_LIST_AND));
 }
 
 static boolean advancedFilter(struct cart *cart, struct trackDb *tdb, char *setting)
 {
 if (!tdbIsBigBed(tdb))
@@ -4523,33 +4524,33 @@
 	    label = (filterBy->valueAndLabel ? slValue->name + strlen(slValue->name)+1
 					     : slValue->name);
 	    name = slValue->name;
 	    }
 	printf("<OPTION");
 	if (filterBy->slChoices != NULL && slNameInList(filterBy->slChoices,name))
 	    printf(" SELECTED");
 	if (filterBy->useIndex || filterBy->valueAndLabel)
 	    printf(" value='%s'",htmlEncode(name));    // filterValues are hub supplied
 	if (filterBy->styleFollows)
 	    {
 	    char *styler = label + strlen(label)+1;
 	    if (*styler != '\0')
 		{
 		if (*styler == '#') // Legacy: just the color that follows
-		    printf(" style='color: %s;'",styler);
+		    printf(" style='color: %s;'",htmlEncode(styler));
 		else
-		    printf(" style='%s'",styler);
+		    printf(" style='%s'",htmlEncode(styler));
 		}
 	    }
 	printf(">%s</OPTION>\n",htmlEncode(label));   // filterValues are hub supplied
 	}
     printf("</SELECT>\n");
     puts("</td>");
     }
 
 puts("</TR></TABLE>");
 }
 
 void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb,
 		  filterBy_t *filterBySet, boolean onOneLine, char *prefix)
 /* Does the filter UI for a list of filterBy structure */
 {
@@ -8539,31 +8540,32 @@
         safef(option, sizeof(option), "%s.%s", name, BASE_COLORS_VAR);
         puts("&nbsp; Alternate colors every");
         cgiMakeIntVar(option, cartCgiUsualInt(cart, option, 0), 1);
         puts("bases<BR>");
         safef(option, sizeof(option), "%s.%s", name,
 			    BASE_COLORS_OFFSET_VAR);
         puts("&nbsp; Offset alternate colors by");
         cgiMakeIntVar(option, cartCgiUsualInt(cart, option, 0), 1);
         puts("bases<BR>");
 #endif
 	}
     }
 
 treeImage = trackDbSetting(tdb, "treeImage");
 if (treeImage)
-    printf("</TD><TD VALIGN=\"TOP\"><IMG SRC=\"../images/%s\"></TD></TR></TABLE>", treeImage);
+    printf("</TD><TD VALIGN=\"TOP\"><IMG SRC=\"../images/%s\"></TD></TR></TABLE>",
+        htmlEncode(treeImage));   // treeImage may be hub supplied
 else
     puts("</TD></TR></TABLE>");
 
 if (trackDbSetting(tdb, CONS_WIGGLE) != NULL)
     {
     wigCfgUi(cart,tdb,name,"Conservation graph:",FALSE);
     }
 cfgEndBox(boxed);
 }
 
 static char *grayLabels[] =
     { "alignment quality",
       "base qualities",
       "unpaired ends",
     };
@@ -9626,31 +9628,32 @@
                     }
                 }
             }
         if (dimensionX && cntX>MATRIX_RIGHT_BUTTONS_AFTER)
             matrixYheadings(db,parentTdb, membersForAll,ixY,FALSE);
         puts("</TR>\n");
         }
     }
 if (dimensionY && cntY>MATRIX_BOTTOM_BUTTONS_AFTER)
     matrixXheadings(db,parentTdb,membersForAll,FALSE);
 
 puts("</TABLE>");
 
 // if there is a treeImage, put it beside the matrix
 if (treeImage != NULL)
-    printf("</TD><TD><IMG SRC=\"%s\"></TD></TABLE>", treeImage);
+    printf("</TD><TD><IMG SRC=\"%s\"></TD></TABLE>",
+        htmlEncode(treeImage));   // treeImage may be hub supplied
 
 // If any filter additional filter composites, they can be added at the end.
 compositeUiByFilter(db, cart, parentTdb, formName);
 
 fastMatixToSubtrackMap();  
 
 return TRUE;
 }
 
 static boolean compositeUiAllButtons(char *db, struct cart *cart, struct trackDb *parentTdb,
                                      char *formName)
 // UI for composite tracks: all/none buttons only (as opposed to matrix or lots of buttons
 {
 if (trackDbCountDescendantLeaves(parentTdb) <= 1)
     return FALSE;
@@ -10275,31 +10278,32 @@
     src = htmlEncode(buffer);
     }
 
 if (setting)
     {
     url = nextWord(&setting);
     if (setting)
         {
         hint = htmlEncode(stripEnclosingDoubleQuotes(setting));
         }
     }
 struct dyString *ds = dyStringNew(0);
 
 // generate markup
 if (url)
-    dyStringPrintf(ds, "<a class='pennantIconText' href='%s' target='ucscHelp' ", url);
+    dyStringPrintf(ds, "<a class='pennantIconText' href='%s' target='ucscHelp' ",
+        htmlEncode(url));   // pennantIcon may be hub supplied
 else if (isTextIcon)
     dyStringAppend(ds, "<span class='pennantIconText' ");
 if (isTextIcon)
     dyStringPrintf(ds, "style='color: %s;' ", color);
 if (hint)
     dyStringPrintf(ds, "title='%s' ", hint);
 if (url || isTextIcon)
     dyStringAppend(ds, ">");
 
 // add text or image
 if (isTextIcon) 
     dyStringPrintf(ds, "%s", src);
 else
     dyStringPrintf(ds, "<img height='16' width='16' src='%s'>", src);