304e190d0af4be54569ac20edc26999673c44f8b braney Tue Aug 18 11:05:19 2026 -0700 hgTrackUi, hui: encode trackDb-derived label text consistently, refs #38123 diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 982ef64ad30..743ed56095a 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -206,41 +206,44 @@ dyStringAppend(dyLink,suffix); // Don't encode since this may contain HTML freeMem(encTerm); freeMem(encValue); return dyStringCannibalize(&dyLink); } char *pairsAsHtmlTable( struct slPair *pairs, struct trackDb *tdb, boolean showLongLabel,boolean showShortLabel) /* Return a string which is an HTML table of the tags for this track. */ { if (pairs == NULL) return ""; struct dyString *dyTable = dyStringCreate(""); +// the labels and the metadata pairs all come from trackDb, which a hub controls, escape if (showLongLabel) - dyStringPrintf(dyTable,"",tdb->longLabel); + dyStringPrintf(dyTable,"", + htmlEncode(tdb->longLabel)); if (showShortLabel) dyStringPrintf(dyTable,"" - "",tdb->shortLabel); + "",htmlEncode(tdb->shortLabel)); for(; pairs; pairs = pairs->next) { if (!sameString(pairs->name, "meta") && !isEmpty((char *)pairs->val)) dyStringPrintf(dyTable,"" - "",pairs->name, (char *)pairs->val); + "",htmlEncode(pairs->name), + htmlEncode((char *)pairs->val)); } dyStringAppend(dyTable,"
%s
%s
shortLabel:%s
%s
%s:%s
%s
"); return dyStringCannibalize(&dyTable); } char *metadataAsHtmlTable(char *db,struct trackDb *tdb,boolean showLongLabel,boolean showShortLabel) // If metadata from metaDb exists, return string of html with table definition { struct slPair *pairs = NULL; if ((pairs = trackDbMetaPairs(tdb)) != NULL) return pairsAsHtmlTable(pairs, tdb, showLongLabel, showShortLabel); const struct mdbObj *safeObj = metadataForTable(db,tdb,NULL); if (safeObj == NULL || safeObj->vars == NULL) @@ -2959,41 +2962,50 @@ } members = needMem(sizeof(members_t)); members->setting = cloneString(setting); #define MAX_SUBGROUP_MEMBERS 2000 char *words[MAX_SUBGROUP_MEMBERS+3]; // members preceded by tag and title, one extra to detect count = chopLine(members->setting, words); if (count == ArraySize(words)) warn("Subgroup %s exceeds maximum %d members", words[1], MAX_SUBGROUP_MEMBERS); if (count <= 1) { freeMem(members->setting); freeMem(members); tdbExtrasMembersSet(parentTdb, groupNameOrTag, &nullMember); return NULL; } +// A subGroup label from a track hub is text from a stranger and is printed into the page in +// a dozen places, so escape it here, once. Only for hub tracks: our own trackDb puts real +// HTML entities in these labels on purpose (the ENCODE composites use   and &alpha), +// and escaping those would show the entity text instead of the character. +boolean escapeLabels = isHubTrack(parentTdb->track); members->groupTag = words[0]; members->groupTitle = strSwapChar(words[1],'_',' '); // Titles replace '_' with space +if (escapeLabels) + members->groupTitle = htmlEncode(members->groupTitle); members->tags = needMem(count*sizeof(char*)); members->titles = needMem(count*sizeof(char*)); for (ix = 2,members->count=0; ix < count; ix++) { char *name,*value; if (parseAssignment(words[ix], &name, &value)) { members->tags[members->count] = tagEncode(name); members->titles[members->count] = strSwapChar(value,'_',' '); + if (escapeLabels) + members->titles[members->count] = htmlEncode(members->titles[members->count]); members->count++; } else { warn("Subgroup \"%s\" is missing a tag=val pair", words[1]); } } tdbExtrasMembersSet(parentTdb, groupNameOrTag, members); return members; } static int membersSubGroupIx(members_t* members, char *tag) // Returns the index of the subgroup within the members struct (or -1) { @@ -4404,34 +4416,35 @@ jsIncludeFile("ui.dropdownchecklist.js",NULL); jsIncludeFile("ddcl.js",NULL); } // TODO: columnCount (Number of filterBoxes per row) should be configurable through tdb setting for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next) { puts(""); char selectStatement[4096]; char *setting = getFilterType(cart, tdb, filterBy->column, FILTERBY_DEFAULT); if (filterByColumnIsMultiple(cart, tdb, setting)) safef(selectStatement, sizeof selectStatement, " (select multiple items - %s)", FILTERBY_HELP_LINK); else selectStatement[0] = 0; + // the title is filterLabel. or an autoSql column comment, both hub supplied if(count == 1) - printf("%s by %s%s",filterTypeTitle,filterBy->title,selectStatement); + printf("%s by %s%s",filterTypeTitle,htmlEncode(filterBy->title),selectStatement); else - printf("%s",filterBy->title); + printf("%s",htmlEncode(filterBy->title)); puts(""); } puts(""); for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next) { puts(""); char *setting = getFilterType(cart, tdb, filterBy->column, FILTERBY_DEFAULT); if (advancedFilter(cart, tdb, setting)) { char cartSettingString[4096]; if (isHighlight) { safef(cartSettingString, sizeof cartSettingString, "%s.%s.%s", prefix,HIGHLIGHT_TYPE_NAME_LOW, filterBy->column); printf("
Highlight if "); // ADVANCED BUTTON printf("