2cdae04ddc1e46cdfc2e5e8cf479cbbbb616ad10 chmalee Tue Sep 22 15:55:10 2026 -0700 htmlSanitize tooltips before printing them into the page, refs #38226 diff --git src/hg/hgTracks/imageV2.c src/hg/hgTracks/imageV2.c index 8b4cc643d0d..3f05652be00 100644 --- src/hg/hgTracks/imageV2.c +++ src/hg/hgTracks/imageV2.c @@ -5,30 +5,31 @@ #include "common.h" #include "hPrint.h" #include "chromInfo.h" #include "hdb.h" #include "hui.h" #include "jsHelper.h" #include "cheapcgi.h" #include "htmshell.h" #include "imageV2.h" #include "hgTracks.h" #include "hgConfig.h" #include "regexHelper.h" #include "customComposite.h" #include "chromAlias.h" #include "trackHub.h" +#include "htmlSanitize.h" // Note: when right-click View image (or pdf output) then theImgBox==NULL, so it will be rendered as a single simple image struct imgBox *theImgBox = NULL; // Make this global for now to avoid huge rewrite struct imgTrack *curImgTrack = NULL; // Make this global for now to avoid huge rewrite ///////////////////////// // FLAT TRACKS // A simplistic way of flattening the track list before building the image // NOTE: Strategy is NOT to use imgBox->imgTracks, since this should be independednt of imageV2 ///////////////////////// void flatTracksAdd(struct flatTracks **flatTracks,struct track *track,struct cart *cart, struct slName *orderedWiggles) // Adds one track into the flatTracks list { struct flatTracks *flatTrack; AllocVar(flatTrack); @@ -1885,46 +1886,56 @@ else if (startsWith("/cgi-bin/hgGene", item->linkVar)) // redmine #4151 hPrintf(" HREF='..%s'",item->linkVar); // FIXME: Chin should get rid else // of this special case! hPrintf(" HREF='%s'",item->linkVar); hPrintf(" class='area'"); } else warn("map item has no url!"); if (item->title != NULL && strlen(item->title) > 0) { char *encodedString = attributeEncode(item->title); if (cfgOptionBooleanDefault("showMouseovers", FALSE)) { if (isNotEmpty(item->tooltip)) - hPrintf(" title='%s' data-tooltip='%s'", encodedString, attributeEncode(item->tooltip)); + { + char *sanitized = htmlSanitize(item->tooltip); + char *encoded = attributeEncode(sanitized); + hPrintf(" title='%s' data-tooltip='%s'", encodedString, encoded); + freeMem(encoded); + freeMem(sanitized); + } else hPrintf(" TITLE='%s'", encodedString); } else { // for TITLEs, which we use for mouseOvers, since they can't have HTML in // them, we substitute a unicode new line for
after we've encoded it. // This is stop-gap until we start doing mouseOvers entirely in Javascript hPrintf(" TITLE='%s'", replaceChars(encodedString,"<br>", "
")); } } else if (isNotEmpty(item->tooltip) && cfgOptionBooleanDefault("showMouseovers", FALSE)) { // some items have no title string (no item name) but do have tooltips - hPrintf(" data-tooltip='%s'", attributeEncode(item->tooltip)); + char *sanitized = htmlSanitize(item->tooltip); + char *encoded = attributeEncode(sanitized); + hPrintf(" data-tooltip='%s'", encoded); + freeMem(encoded); + freeMem(sanitized); } if (item->id != NULL) hPrintf(" id='%s'", item->id); hPrintf(">" ); } hPrintf("\n"); return TRUE; } static void imageDraw(struct imgBox *imgBox,struct imgTrack *imgTrack,struct imgSlice *slice, char *name,int offsetX,int offsetY,boolean useMap) // writes an image as HTML { if (slice->parentImg && slice->parentImg->file != NULL) { @@ -2080,45 +2091,49 @@ struct mapSet *map = sliceGetMap(slice,FALSE); // Could be the image map or slice specific if (map) useMap = imageMapDraw(map,name); else if (slice->link != NULL) { if (sameString(TITLE_BUT_NO_LINK,slice->link)) { // This fake link ensures a mouse-over title is seen but not heard hPrintf("link) != NULL) hPrintf(" link); else hPrintf(" link); if (slice->title != NULL) { + char *sanitized = htmlSanitize(slice->title); + char *encSanitized = attributeEncode(sanitized); if (sliceType == stButton) { enum browserType browser = cgiClientBrowser(NULL,NULL,NULL); char *newLine = NEWLINE_TO_USE(browser); char *ellipsis = ELLIPSIS_TO_USE(browser); if (imgTrack->reorderable) hPrintf(" TITLE='%s%sclick or right click to configure%s%sdrag to reorder%s'", - attributeEncode(slice->title), newLine, ellipsis, newLine, + encSanitized, newLine, ellipsis, newLine, (tdbIsCompositeChild(imgTrack->tdb) ? " highlighted subtracks" : "") ); else hPrintf(" TITLE='%s%sclick or right click to configure%s'", - attributeEncode(slice->title), newLine, ellipsis); + encSanitized, newLine, ellipsis); } else - hPrintf(" TITLE='Click for: %s'", attributeEncode(slice->title) ); + hPrintf(" TITLE='Click for: %s'", encSanitized ); + freeMem(sanitized); + freeMem(encSanitized); } hPrintf(">\n" ); } char *trackName = (imgTrack->name != NULL ? imgTrack->name : imgTrack->tdb->track ); struct jsonElement *trackHash = jsonFindNamedField(ele, "trackDb", trackName); jsonObjectAdd(trackHash, "imgOffsetY", newJsonNumber(offsetY)); imageDraw(imgBox,imgTrack,slice,name,offsetX,offsetY,useMap); if (slice->link != NULL) hPrintf(""); if (slice->parentImg) hPrintf(""); } @@ -2229,75 +2244,83 @@ jsonTdbSettingsInit(jsonTdbVars); char *newLine = NEWLINE_TO_USE(cgiClientBrowser(NULL,NULL,NULL)); struct imgTrack *imgTrack = smashSquish(imgBox->imgTracks); for (;imgTrack!=NULL;imgTrack=imgTrack->next) { char *trackName = (imgTrack->name != NULL ? imgTrack->name : imgTrack->tdb->track ); struct track *track = hashFindVal(trackHash, trackName); if (track) jsonTdbSettingsBuild(jsonTdbVars, track, TRUE); hPrintf("\n",trackName,imgTrack->order, (imgTrack->reorderable ? " trDraggable" : " nodrop nodrag"), (imgTrack->centerLabelSeen != clAlways ? " clOpt" : ""), (imgTrack->ajaxRetrieval ? " mustRetrieve" : "")); + char *sanitized = NULL, *encSanitized = NULL; + if (imgTrack->reorderable) + { + sanitized = htmlSanitize(imgTrack->tdb->longLabel); + encSanitized = attributeEncode(sanitized); + } if (imgBox->showSideLabel && imgBox->plusStrand) { // button safef(name, sizeof(name), "btn_%s", trackName); hPrintf(" \n",name,(imgTrack->reorderable ? " class='dragHandle'" : "")); sliceAndMapDraw(imgBox,imgTrack,stButton,name,FALSE, jsonTdbVars); hPrintf("\n"); // leftLabel safef(name,sizeof(name),"side_%s",trackName); if (imgTrack->reorderable) hPrintf(" \n", - name,attributeEncode(imgTrack->tdb->longLabel),newLine); + name,encSanitized,newLine); else hPrintf(" \n",name); sliceAndMapDraw(imgBox,imgTrack,stSide,name,FALSE, jsonTdbVars); if (cfgOptionBooleanDefault("greyBarIcons", TRUE)) hPrintf("", trackName); hPrintf("\n"); } // Main/Data image region hPrintf(" \n", trackName, imgBox->width); // centerLabel if (imgTrack->hasCenterLabel) { safef(name, sizeof(name), "center_%s", trackName); sliceAndMapDraw(imgBox,imgTrack,stCenter,name,TRUE, jsonTdbVars); hPrintf("\n"); } // data image safef(name, sizeof(name), "data_%s", trackName); sliceAndMapDraw(imgBox,imgTrack,stData,name,(imgTrack->order>0), jsonTdbVars); hPrintf("\n"); if (imgBox->showSideLabel && !imgTrack->plusStrand) { // rightLabel safef(name, sizeof(name), "side_%s", trackName); if (imgTrack->reorderable) hPrintf(" \n", - name,attributeEncode(imgTrack->tdb->longLabel),newLine); + name,encSanitized,newLine); else hPrintf(" \n",name); sliceAndMapDraw(imgBox,imgTrack,stSide,name,FALSE, jsonTdbVars); if (cfgOptionBooleanDefault("greyBarIcons", TRUE)) hPrintf("", trackName); hPrintf("\n"); // button safef(name, sizeof(name), "btn_%s", trackName); hPrintf(" \n",name,(imgTrack->reorderable ? " class='dragHandle'" : "")); sliceAndMapDraw(imgBox,imgTrack,stButton, name,FALSE, jsonTdbVars); hPrintf("\n"); } + freeMem(encSanitized); + freeMem(sanitized); hPrintf("\n"); } hPrintf("\n"); hPrintf("\n"); jsonTdbSettingsUse(jsonTdbVars); }