a1d7c07c50f1d895337de121680ea672b261c058
max
  Mon Aug 17 02:26:36 2026 -0700
escape reflected/echoed user input across several CGIs (XSS), refs #38057

Route user-, DB- and hub-derived values through htmlEncode (HTML/attribute text),
cgiEncode (values composed into URLs), jsonStringEscape (values placed in a JS string
literal inside an inline script) or, for hgMirror, the existing mustBeClean sanitizer.
Covers hgHubConnect, hgUserSuggestion, hgLiftOver, hgBlat, hgc pubs, hgVisiGene,
hgSession, hgTrackUi, hgGenome, phyloPng, hgFileSearch, hgLinkIn, hgPal, hui,
hgPhyloPlace, hgMirror, hgCustom and hgSearch.

diff --git src/hg/hgc/pubs.c src/hg/hgc/pubs.c
index 1c008a4b35a..7106b7361ff 100644
--- src/hg/hgc/pubs.c
+++ src/hg/hgc/pubs.c
@@ -1,22 +1,23 @@
 /* pubs.c - display details of publiations literature track (pubsxxx tables) */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "jksql.h"
+#include "htmshell.h"
 #include "hdb.h"
 #include "hgc.h"
 #include "hgColors.h"
 #include "trackDb.h"
 #include "web.h"
 #include "hash.h"
 #include "net.h"
 #include "obscure.h"
 #include "common.h"
 #include "string.h"
 #include "dystring.h"
 #include "dnautil.h"
 #include "trackHub.h"
 //#include "ctype.h"
 
@@ -340,52 +341,54 @@
 sqlSafef(query, sizeof(query), "SELECT section, snippet FROM %s "  
     "WHERE articleId=%s AND markerId='%s' AND section in (%-s) ", 
     markerTable, articleId, markerId, sectionList);
 struct sqlResult *sr = sqlGetResult(conn, query);
 return sr;
 }
 
 static void printSectionCheckboxes()
 /* show a little form with checkboxes where user can select sections they want to show */
 {
 int labelCount = sizeof(secLabels)/sizeof(char *);
 
 int i;
 printf("<P>\n");
 printf("<B>Sections of article searched:</B><BR>\n");
+// o/t/g/i are user-supplied and composed into a URL in an attribute; cgiEncode them (XSS)
 printf("<FORM ACTION=\"hgc?%s&o=%s&t=%s&g=%s&i=%s\" METHOD=\"get\">\n",
-    cartSidUrlString(cart), cgiString("o"), cgiString("t"), cgiString("g"), cgiString("i"));
+    cartSidUrlString(cart), cgiEncode(cgiString("o")), cgiEncode(cgiString("t")),
+    cgiEncode(cgiString("g")), cgiEncode(cgiString("i")));
 
 for (i=0; i<labelCount; i++) 
 {
     char *name = pubsSecNames[i];
     // checkboxes default to 0 unless checked, see 
     // http://stackoverflow.com/questions/2520952/how-come-checkbox-state-is-not-always-passed-along-to-php-script
     printf("<INPUT TYPE=\"hidden\" name=\"%s\" value=\"0\" />\n", pubsSecNames[i]);
     printf("<INPUT TYPE=\"checkbox\" name=\"%s\" ", name);
 
     int isChecked = cgiOptionalInt(name, pubsSecChecked[i]);
     if (isChecked)
         printf("value=\"1\" checked=\"yes\">%s</INPUT>\n", secLabels[i]);
     else
         printf("value=\"1\">%s</INPUT>\n", secLabels[i]);
 }
 
-printf("<INPUT TYPE=\"hidden\" name=\"o\" value=\"%s\" />\n", cgiString("o"));
-printf("<INPUT TYPE=\"hidden\" name=\"g\" value=\"%s\" />\n", cgiString("g"));
-printf("<INPUT TYPE=\"hidden\" name=\"t\" value=\"%s\" />\n", cgiString("t"));
-printf("<INPUT TYPE=\"hidden\" name=\"i\" value=\"%s\" />\n", cgiString("i"));
+printf("<INPUT TYPE=\"hidden\" name=\"o\" value=\"%s\" />\n", htmlEncode(cgiString("o")));
+printf("<INPUT TYPE=\"hidden\" name=\"g\" value=\"%s\" />\n", htmlEncode(cgiString("g")));
+printf("<INPUT TYPE=\"hidden\" name=\"t\" value=\"%s\" />\n", htmlEncode(cgiString("t")));
+printf("<INPUT TYPE=\"hidden\" name=\"i\" value=\"%s\" />\n", htmlEncode(cgiString("i")));
 printf("<INPUT TYPE=\"hidden\" name=\"hgsid\" value=\"%s\" />\n", cart->sessionId);
 printf("<BR>");
 printf("<INPUT TYPE=\"submit\" VALUE=\"Submit\" />\n");
 printf("</FORM><P>\n");
 }
 
 static void printLimitWarning(struct sqlConnection *conn, char *markerTable, 
     char *item, int itemLimit, char *sectionList)
 {
 char query[4000];
 // no need to check for illegal characters in sectionList
 sqlSafef(query, sizeof(query), "SELECT COUNT(*) from %s WHERE markerId='%s' AND section in (%-s) ", markerTable, item, sectionList);
 if (sqlNeedQuickNum(conn, query) > itemLimit) 
     {
     printf("<b>This marker is mentioned more than %d times</b><BR>\n", itemLimit);