c5af95b7146dde46f2d2f70ecad1eb9b4c37b57f
galt
  Mon Feb 3 17:36:09 2025 -0800
Revert "Fixing security concern in hgEncodeVocab. fixes #287. Note that actual full cleanup by removing the unneeded encode/cv.ra from trackDb files has not been done yet., and making the code tolerate its presence or absence in the trackDb.ra files, at the start of the controlledVocabulary setting."

This reverts commit 156dbcfc96c9a4a5eba481f8d979700b0ca1024e.

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 8a89d8db1ea..5d1c4bbeae8 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -174,38 +174,47 @@
     // until it's implemented
     {
     char *tbOff = trackDbSetting(tdb, "tableBrowser");
     if (isNotEmpty(tbOff) && sameString(nextWord(&tbOff), "off"))
 	return FALSE;
     char *hint = " title='Open data format (table schema) in new window'";
     if (label == NULL)
 	label = " View data format";
     struct trackDb *topLevel = trackDbTopLevelSelfOrParent(tdb);
     printf(SCHEMA_LINKED, db, topLevel->grp, topLevel->track, tdb->table, hint, label);
     return TRUE;
     }
 return FALSE;
 }
 
-char *wgEncodeVocabLink(char *term,char *value,char *title, char *label,char *suffix)
+char *wgEncodeVocabLink(char *file,char *term,char *value,char *title, char *label,char *suffix)
 // returns allocated string of HTML link to controlled vocabulary term
 {
+#define VOCAB_LINK_WITH_FILE "<A HREF='hgEncodeVocab?ra=%s&%s=\"%s\"' title='%s details' " \
+			 "class='cv' TARGET=ucscVocab>%s</A>"
 #define VOCAB_LINK "<A HREF='hgEncodeVocab?%s=\"%s\"' title='%s details' class='cv' " \
 	       "TARGET=ucscVocab>%s</A>"
 struct dyString *dyLink = NULL;
 char *encTerm = cgiEncode(term);
 char *encValue = cgiEncode(value);
+if (file != NULL)
+    {
+    char *encFile = cgiEncode(file);
+    dyLink = dyStringCreate(VOCAB_LINK_WITH_FILE,encFile,encTerm,encValue,title,label);
+    freeMem(encFile);
+    }
+else
     dyLink = dyStringCreate(VOCAB_LINK,encTerm,encValue,title,label);
 if (suffix != NULL)
     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 "";
 
@@ -274,36 +283,36 @@
 		dyStringAppend(dyTable,"<BR>");
 	    slNameFree(&file);
 	    }
 	dyStringAppend(dyTable,"</td></tr>");
 	}
     else
 	{                                           // Don't bother with tableName
 	if (cvTermTypes && differentString(mdbVar->var,MDB_VAR_TABLENAME))
 	    {
 	    struct hash *cvTerm = hashFindVal(cvTermTypes,mdbVar->var);
 	    if (cvTerm != NULL) // even if cvTerm isn't used,
 		{               // it proves that it exists and a link is desirable
 		if (!cvTermIsHidden(mdbVar->var))
 		    {
 		    char *label = (char *)cvLabel(NULL,mdbVar->var);
-		    char *linkOfType = wgEncodeVocabLink(CV_TYPE,mdbVar->var,label,
+		    char *linkOfType = wgEncodeVocabLink(NULL,CV_TYPE,mdbVar->var,label,
 							   label,NULL);
 		    if (cvTermIsCvDefined(mdbVar->var))
 			{
 			label = (char *)cvLabel(mdbVar->var,mdbVar->val);
-			char *linkOfTerm = wgEncodeVocabLink(CV_TERM,mdbVar->val,label,
+			char *linkOfTerm = wgEncodeVocabLink(NULL,CV_TERM,mdbVar->val,label,
 							       label,NULL);
 			dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap>"
 					       "<i>%s:</i></td><td nowrap>%s</td></tr>",
 					       linkOfType,linkOfTerm);
 			freeMem(linkOfTerm);
 			}
 		    else
 			dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap>"
 					       "<i>%s:</i></td><td nowrap>%s</td></tr>",
 					       linkOfType,mdbVar->val);
 		    freeMem(linkOfType);
 		    continue;
 		    }
 		}
 	    }
@@ -8563,44 +8572,44 @@
 char *words[SMALLBUF];
 int count;
 if ((count = chopByWhite(cloneString(vocab), words, SMALLBUF)) <= 1)
     return cloneString(label);
 
 
 char *suffix = NULL;
 char *rootLabel = labelRoot(label, &suffix);
 
 boolean found = FALSE;
 int ix;
 for (ix=1;ix<count && !found;ix++)
     {
     if (sameString(vocabType,words[ix])) // controlledVocabulary setting matches tag
         {                               // so all labels are linked
-        char *link = wgEncodeVocabLink("term",words[ix],rootLabel,rootLabel,suffix);
+        char *link = wgEncodeVocabLink(words[0],"term",words[ix],rootLabel,rootLabel,suffix);
         return link;
         }
     else if (countChars(words[ix],'=') == 1 && childTdb != NULL)
             // The name of a trackDb setting follows and will be the controlled vocab term
         {
         strSwapChar(words[ix],'=',0);
         if (sameString(vocabType,words[ix]))  // tags match, but search for term
             {
             char * cvSetting = words[ix] + strlen(words[ix]) + 1;
             const char * cvTerm = metadataFindValue(childTdb,cvSetting);
             if (cvTerm != NULL)
                 {
-                char *link = wgEncodeVocabLink(
+                char *link = wgEncodeVocabLink(words[0],
                                     (sameWord(cvSetting,"antibody") ?  "target" : "term"),
                                     (char *)cvTerm,(char *)cvTerm,rootLabel,suffix);
                 return link;
                 }
             }
         }
     }
 freeMem(words[0]);
 freeMem(rootLabel);
 return cloneString(label);
 }
 
 #define PM_BUTTON_UC "<IMG height=18 width=18 id='%s' src='../images/%s'>"
 #define PM_BUTTON_UC_JS "return (matSetMatrixCheckBoxes(%s%s%s%s%s%s) == false);" 
 #define PM_MAKE_BUTTON_UC(s1,s2,s3,s4,s5,s6,name,img) \
@@ -8930,33 +8939,33 @@
         mdbVar = members->groupTag;
         break;
         }
     else if (startsWithWordByDelimiter(members->groupTag,'=',words[ix]))
         {
         mdbVar = words[ix] + strlen(members->groupTag) + 1;
         break;
         }
     }
 if (mdbVar == NULL)
     {
     freeMem(vocab);
     return cloneString(members->groupTitle);
     }
 
-#define VOCAB_MULTILINK_BEG "<A HREF='hgEncodeVocab?%s=\""
+#define VOCAB_MULTILINK_BEG "<A HREF='hgEncodeVocab?ra=%s&%s=\""
 #define VOCAB_MULTILINK_END "\"' title='Click for details of each %s' TARGET=ucscVocab>%s</A>"
-struct dyString *dyLink = dyStringCreate(VOCAB_MULTILINK_BEG,
+struct dyString *dyLink = dyStringCreate(VOCAB_MULTILINK_BEG,vocab,
                                          (sameWord(mdbVar,"antibody")?"target":"term"));
 
 // Now build the comma delimited string of mdb vals (all have same mdb var)
 boolean first = TRUE;
 for (ix=0;ix<members->count;ix++)
     {
     if (members->subtrackList[ix] != NULL && members->subtrackList[ix]->val != NULL)
         {
         struct trackDb *childTdb = members->subtrackList[ix]->val;
         (void)metadataForTable(db,childTdb,NULL); // Makes sure this has been populated
         const char * mdbVal = metadataFindValue(childTdb,mdbVar); // one for each is enough
         if (mdbVal != NULL)
             {
             if (!first)
                 dyStringAppendC(dyLink,',');