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/hgCustom/hgCustom.c src/hg/hgCustom/hgCustom.c
index d00bacf4ba7..160024cb0b9 100644
--- src/hg/hgCustom/hgCustom.c
+++ src/hg/hgCustom/hgCustom.c
@@ -599,32 +599,34 @@
else
puts("
| ");
}
/* Pos field; indicates initial position for the track,
* or first element */
if (posCt)
{
pos = ctInitialPosition(ct);
if (!pos)
pos = ctFirstItemPos(ct);
if (pos)
{
char *chrom = cloneString(pos);
chopSuffixAt(chrom, ':');
if (hgOfficialChromName(database, chrom))
- printf("%s: | ",
- hgTracksName(), cartSidUrlString(cart),pos, pos, chrom);
+ // pos comes from the custom track; cgiEncode it in the URL and quote+escape the
+ // TITLE attribute (was unquoted) before echoing (XSS). chrom is validated above.
+ printf("%s: | ",
+ hgTracksName(), cartSidUrlString(cart), cgiEncode(pos), htmlEncode(pos), chrom);
else
puts(" | ");
}
else
puts(" | ");
}
if (errCt)
{
if (ct->networkErrMsg)
{
char id[256];
safef(id, sizeof id, "_%d", butCount);
printf("\nShow | \n", id);
jsOnEventByIdF("click", id, "alert('%s');return false;",
javaScriptLiteralEncode(ct->networkErrMsg));
@@ -636,31 +638,31 @@
printf("", showAllButtons ? 2 : 1);
safef(buf, sizeof(buf), "%s_%s", hgCtDeletePrefix,
ct->tdb->track);
cgiMakeCheckBoxMore(buf, setAllDelete, "class='deleteCheckbox'");
puts(" | ");
/* Update checkboxes */
if (updateCt)
{
printf("", showAllButtons ? 2 : 1);
safef(buf, sizeof(buf), "%s_%s", hgCtRefreshPrefix,
ct->tdb->track);
if ((dataUrl = ctDataUrl(ct)) != NULL)
{
char more[2048];
- safef(more, sizeof(more), "class='updateCheckbox' title='refresh data from: %s'", dataUrl);
+ safef(more, sizeof(more), "class='updateCheckbox' title='refresh data from: %s'", htmlEncode(dataUrl)); // user URL into attr, escape (XSS)
cgiMakeCheckBoxMore(buf, setAllUpdate, more);
}
else
puts(" ");
puts(" | ");
}
puts("\n");
}
if (showAllButtons)
{
cgiSimpleTableRowStart();
printf("check all / clear all | ", colSpan);
cgiSimpleTableFieldStart();
cgiMakeButtonWithOnClick(hgCtDoDeleteSet, "+", "Select all for deletion", "$('.deleteCheckbox').attr('checked', true); return false;");
cgiTableFieldEnd();