f2d3014a3cc95116346eab4675817cf0c65f6d62 braney Tue Aug 18 11:12:35 2026 -0700 hgc: encode item detail text consistently, refs #38123 diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index bb86647bed5..4222db1b9c7 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -296,30 +296,43 @@ char mousedb[] = "mm3"; #define NUMTRACKS 9 int prevColor[NUMTRACKS]; /* used to optimize color change html commands */ int currentColor[NUMTRACKS]; /* used to optimize color change html commands */ int maxShade = 9; /* Highest shade in a color gradient. */ Color shadesOfGray[10+1]; /* 10 shades of gray from white to black */ Color shadesOfRed[16]; boolean exprBedColorsMade = FALSE; /* Have the shades of red been made? */ int maxRGBShade = 16; struct bed *sageExpList = NULL; char ncbiOmimUrl[255] = {"https://www.ncbi.nlm.nih.gov/omim/"}; +char *hubEncode(struct trackDb *tdb, char *text) +/* Return text escaped for HTML if it belongs to a track hub, otherwise return it unchanged. + * A hub's trackDb, autoSql schema and data file are all written by a stranger, so anything + * from them has to be escaped before it goes in the page. Our own tracks are a + * different case: they put real HTML in fields on purpose - ClinVar's review-status stars, + * the CRISPR track's links in an extra column, the
in the Denisova schema comments - + * and escaping those would print the markup instead of rendering it. */ +{ +if (text != NULL && tdb != NULL && isHubTrack(tdb->track)) + return htmlEncode(text); +return text; +} + struct palInfo { char *chrom; int left; int right; char *rnaName; }; /* See this NCBI web doc for more info about entrezFormat: * https://www.ncbi.nlm.nih.gov/entrez/query/static/linking.html */ char *entrezFormat = "https://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Search&db=%s&term=%s&doptcmdl=%s&tool=genome.ucsc.edu"; char *entrezPureSearchFormat = "https://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=PureSearch&db=%s&details_term=%s[%s] "; char *ncbiGeneFormat = "https://www.ncbi.nlm.nih.gov/gene/%s"; char *entrezUidFormat = "https://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=%s&list_uids=%d&dopt=%s&tool=genome.ucsc.edu"; @@ -819,38 +832,39 @@ // its bed-compatible leading fields (only the first bedSize fields are read). // At -O3 GCC's -Warray-bounds flags those casts because the real object is // smaller than struct bed; the accesses are safe by the bed-layout convention. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" void bedPrintPos(struct bed *bed, int bedSize, struct trackDb *tdb) /* Print first bedSize fields of a bed type structure in * standard format. */ { char *strand = NULL; if (bedSize >= 4 && bed->name[0] != 0) { char *label = "Item", *tdbLabel = NULL; if (tdb && ((tdbLabel = trackDbSetting(tdb, "bedNameLabel")) != NULL)) label = tdbLabel; - printf("%s: %s
\n", label, bed->name); + // bedNameLabel is a trackDb setting and the name comes from the data file + printf("%s: %s
\n", hubEncode(tdb, label), hubEncode(tdb, bed->name)); } if (bedSize >= 5) { if (!tdb || !trackDbSetting(tdb, "noScoreFilter")) { char *scoreLabel = trackDbSettingOrDefault(tdb, "scoreLabel", "Score"); - printf("%s: %d
\n", scoreLabel, bed->score); + printf("%s: %d
\n", hubEncode(tdb, scoreLabel), bed->score); } } if (bedSize >= 6) { strand = bed->strand; } printPos(bed->chrom, bed->chromStart, bed->chromEnd, strand, TRUE, bed->name); } #pragma GCC diagnostic pop void genericHeader(struct trackDb *tdb, char *item) /* Put up generic track info. */ { if (item != NULL && item[0] != 0) @@ -987,50 +1001,53 @@ char *eUrl = replaceInUrl(url, itemName, cart, database, seqName, winStart, winEnd, tdb->track, encode, fields); if (eUrl==NULL) return; /* create the url label setting for trackDb from the url setting prefix */ safef(urlLabelSetting, sizeof(urlLabelSetting), "%sLabel", urlSetting); char *linkLabel = trackDbSettingOrDefault(tdb, urlLabelSetting, "Outside Link:"); char *eLinkLabel = replaceInUrl(linkLabel, itemName, cart, database, seqName, winStart, winEnd, tdb->track, encode, fields); // if we got no item name from hgTracks or the item name does not appear in the URL // there is no need to show the item name at all +// the url and its label come from trackDb and the item name from the data file, both of +// which a track hub supplies, so escape them if (isEmpty(itemName) || !stringIn("$$", url)) { - printf("%s
",eUrl, eLinkLabel); + printf("%s
",hubEncode(tdb, eUrl), + hubEncode(tdb, eLinkLabel)); return; } -printf("%s ",eLinkLabel); +printf("%s ",hubEncode(tdb, eLinkLabel)); -printf("", eUrl); +printf("", hubEncode(tdb, eUrl)); if (sameWord(tdb->table, "npredGene")) { printf("%s (%s)
\n", itemName, "NCBI MapView"); } else { char *label = itemName; if (isNotEmpty(itemLabel) && differentString(itemName, itemLabel)) label = itemLabel; - printf("%s
\n", label); + printf("%s
\n", hubEncode(tdb, label)); } //freeMem(&eUrl); small memory leak } void printCustomUrlWithFields(struct trackDb *tdb, char *itemName, char *itemLabel, boolean encode, struct slPair *fields) /* Wrapper to call printCustomUrlWithLabel with additional fields to substitute */ { char urlSetting[10]; safef(urlSetting, sizeof(urlSetting), "url"); printCustomUrlWithLabel(tdb, itemName, itemLabel, urlSetting, encode, fields); } void printCustomUrl(struct trackDb *tdb, char *itemName, boolean encode) /* Wrapper to call printCustomUrlWithLabel using the url setting in trackDb */ @@ -1460,31 +1477,31 @@ return idNames; } void printIdOrLinks(struct asColumn *col, struct hash *fieldToUrl, struct trackDb *tdb, char *idList) /* if trackDb does not contain a "urls" entry for current column name, just print idList as it is. * Otherwise treat idList as a comma-sep list of IDs and print one row per id, with a link to url, * ($$ in url is OK, wildcards like $P, $p, are also OK) * */ { // try to find a fieldName=url setting in the "urls" tdb statement, print id if not found char *url = NULL; if (fieldToUrl != NULL) url = (char*)hashFindVal(fieldToUrl, col->name); if (url == NULL) { - printf("%s\n", idList); + printf("%s\n", hubEncode(tdb, idList)); return; } // split the id into parts and print each part as a link struct slName *slIds = slNameListFromComma(idList); struct slName *itemId = NULL; // handle id->name mapping for multi-source items int nameCount; char **idNames = getIdNameMap(tdb, col, &nameCount); printf(""); for (itemId = slIds; itemId!=NULL; itemId = itemId->next) { if (itemId != slIds) @@ -1502,31 +1519,32 @@ char *idForUrl = itemName; boolean encode = TRUE; if (strstr(itemName, "|")) { char *parts[2]; chopString(itemName, "|", parts, ArraySize(parts)); idForUrl = parts[0]; itemName = parts[1]; encode = FALSE; // assume the link is already encoded } if (startsWith("http", itemName)) // the ID may be a full URL already, encoding would destroy it encode = FALSE; char *idUrl = replaceInUrl(url, idForUrl, cart, database, seqName, winStart, winEnd, tdb->track, encode, NULL); - printf("%s", idUrl, itemName); + printf("%s", hubEncode(tdb, idUrl), + hubEncode(tdb, itemName)); } printf("\n"); freeMem(slIds); //freeMem(idNames); } char *readOneLineMaybeBgzip(char *fileOrUrl, bits64 offset, bits64 len) /* If fileOrUrl is bgzip-compressed and indexed, then use htslib's bgzf functions to * retrieve uncompressed data from offset; otherwise (plain text) use udc. If len is 0, * read up to next '\n' delimiter. */ { char *line = needMem(len+1); if (endsWith(fileOrUrl, ".gz")) { BGZF *fp = bgzf_open(fileOrUrl, "r"); @@ -1970,47 +1988,49 @@ printf("
"); // split this table to separate current row from the previous one, if the trackDb option is set if (sepFields && slNameInList(sepFields, fieldName)) printf("
\n

\n"); // field description char *entry; if (sameString(fieldName, "cdsStartStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment)) entry = "Status of CDS start annotation (none, unknown, incomplete, or complete)"; else if (sameString(fieldName, "cdsEndStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment)) entry = "Status of CDS end annotation (none, unknown, incomplete, or complete)"; else entry = col->comment; - printFieldLabelWithId(entry, fieldName); + // the field name and its comment come from the autoSql schema, which for a hub bigBed + // is written by the hub author + printFieldLabelWithId(hubEncode(tdb, entry), hubEncode(tdb, fieldName)); // detailsScript fields: print empty cell, JavaScript will fill it if (dsScriptFields && slNameInList(dsScriptFields, fieldName)) printf("\n"); else if (col->isList || col->isArray || col->lowType->stringy || asTypesIsInt(col->lowType->type)) printIdOrLinks(col, fieldToUrl, tdb, fields[ix]); else if (asTypesIsFloating(col->lowType->type)) { double valDouble = strtod(fields[ix],NULL); if (errno == 0 && valDouble != 0) printf("\n", valDouble); else - printf("\n", fields[ix]); // decided not to print error + printf("\n", hubEncode(tdb, fields[ix])); // decided not to print error } else - printf("\n", fields[ix]); + printf("\n", hubEncode(tdb, fields[ix])); printCount++; } if (skipIds) slFreeList(skipIds); if (sepFields) slFreeList(sepFields); if (embeddedTblFields) { printf("
%g
%s
%s
%s
%s
\n"); struct embeddedTbl *thisTbl; struct dyString *tableLabelsDy = dyStringNew(0); boolean initted = FALSE; for (thisTbl = embeddedTblList; thisTbl != NULL; thisTbl = thisTbl->next) @@ -3663,33 +3683,35 @@ "&hgta_table=%s&position=%s:%d-%d&" "hgta_doSchema=describe+table+schema\" target=ucscSchema title='Open schema in new window'>" "View table schema

\n", database, tdb->grp, trackTable, tdb->table, seqName, winStart+1, winEnd); } } void printTrackUiLink(struct trackDb *tdb) /* Make link to hgTrackUi. */ { char *trackName = getParentTrackName(tdb); struct trackDb *parentTdb = tdb; if (!sameString(trackName, tdb->track)) parentTdb = hTrackDbForTrack(database, trackName); +// shortLabel comes from trackDb, which a track hub controls, escape it printf("

" "Go to %s track controls

\n", - hTrackUiForTrack(tdb->track), database, trackName, cartSidUrlString(cart), parentTdb->shortLabel); + hTrackUiForTrack(tdb->track), database, trackName, cartSidUrlString(cart), + htmlEncode(parentTdb->shortLabel)); } void printDataRestrictionDate(struct trackDb *tdb) /* If this annotation has a dateUnrestricted trackDb setting, print it */ { char *restrictionDate = encodeRestrictionDateDisplay(database,tdb); if (restrictionDate != NULL) { printf("Restricted until: %s
\n", restrictionDate); freeMem(restrictionDate); } } static void printOrigAssembly(struct trackDb *tdb)