53621f3c5ef75891a7b04cf18d85180677a38f23
chmalee
Wed Apr 8 11:38:53 2026 -0700
Fix bug in hgc user defined table printing where we overwrote the variable that held the data for each custom table. Also fix a XSS vector with the table titles and some related code cleanup, refs #37340
diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index 84614360b20..ed9efe08ccb 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -1636,36 +1636,39 @@
{
if (startsWith("_json", thisTbl->field) || startsWith("json", thisTbl->field))
{
struct jsonElement *jsElem = NULL;
struct errCatch *errCatch = errCatchNew();
if (errCatchStart(errCatch))
jsElem = jsonParse(thisTbl->encodedTbl);
errCatchEnd(errCatch);
if (errCatch->gotError)
warn("ERROR: JSON field '%s' for track '%s' is malformed: %s", thisTbl->field, tdb->track, errCatch->message->string);
else if (errCatch->gotWarning)
warn("Warning: %s", errCatch->message->string);
errCatchFree(&errCatch);
if (jsElem != NULL)
{
- dyStringPrintf(dy, "{label: \"%s\", data: %s},", thisTbl->title != NULL ? thisTbl->title : thisTbl->field, thisTbl->encodedTbl);
+ char *labelStr = thisTbl->title != NULL ?
+ jsonStringEscape(thisTbl->title) :
+ jsonStringEscape(thisTbl->field);
+ dyStringPrintf(dy, "{label: \"%s\", data: %s},", labelStr, thisTbl->encodedTbl);
}
}
else
{
- printf("
| %s | ", thisTbl->title);
+ printf(" |
| %s | ", htmlEncode(thisTbl->title));
printf("\n");
printf("| ");
char table[4096];
safef(table, sizeof(table), "%s", thisTbl->encodedTbl);
int swapped = strSwapStrs(table, 4096, ";", " | | ");
if (swapped == -1)
errAbort("Error substituting ';' for ' | ' in hgc.c:printEmbeddedTable()");
swapped = strSwapStrs(table, 4096, "|", "| ");
if (swapped == -1)
errAbort("Error substituting '|' for ' | ' in hgc.c:printEmbeddedTable()");
printf("%s | \n", table);
printf(" \n");
printf(" |
\n");
}
}
@@ -1713,46 +1716,48 @@
return foundFields;
}
#define TDB_DYNAMICTABLE_SETTING "detailsDynamicTable"
#define TDB_DYNAMICTABLE_SETTING_2 "extraTableFields"
void getExtraTableFields(struct trackDb *tdb, struct slName **retFieldNames, struct embeddedTbl **retList, struct hash *embeddedTblHash)
/* Parse the trackDb field TDB_DYNAMICTABLE_FIELD into the field names and titles specified,
* and fill out a hash keyed on the bigBed field name (which may be in an external file
* and not in the bigBed itself) to a helper struct for storing user defined tables. */
{
struct slName *tmp, *embeddedTblSetting = slNameListFromComma(trackDbSetting(tdb, TDB_DYNAMICTABLE_SETTING));
struct slName *embeddedTblSetting2 = slNameListFromComma(trackDbSetting(tdb, TDB_DYNAMICTABLE_SETTING_2));
char *title = NULL, *fieldName = NULL;
for (tmp = embeddedTblSetting; tmp != NULL; tmp = tmp->next)
{
+ title = NULL;
fieldName = cloneString(tmp->name);
if (strchr(tmp->name, '|'))
{
title = strchr(fieldName, '|');
*title++ = 0;
}
struct embeddedTbl *new;
AllocVar(new);
new->field = fieldName;
new->title = title != NULL ? cloneString(title) : fieldName;
slAddHead(retList, new);
slNameAddHead(retFieldNames, fieldName);
hashAdd(embeddedTblHash, fieldName, new);
}
for (tmp = embeddedTblSetting2; tmp != NULL; tmp = tmp->next)
{
+ title = NULL;
fieldName = cloneString(tmp->name);
if (strchr(tmp->name, '|'))
{
title = strchr(fieldName, '|');
*title++ = 0;
}
struct embeddedTbl *new;
AllocVar(new);
new->field = fieldName;
new->title = title != NULL ? cloneString(title) : fieldName;
slAddHead(retList, new);
slNameAddHead(retFieldNames, fieldName);
hashAdd(embeddedTblHash, fieldName, new);
}
}
@@ -1986,39 +1991,45 @@
else
printf("\n", fields[ix]);
printCount++;
}
if (skipIds)
slFreeList(skipIds);
if (sepFields)
slFreeList(sepFields);
if (embeddedTblFields)
{
printf("
\n");
if (detailsTableFields)
{
printExtraDetailsTable(tdb->track, extraDetailsTableName, extraDetails, extraTblStr);
}
if (detailsTable2Fields)
{
printExtraDetailsTable(tdb->track, extraDetails2TableName, extraDetails2, extraTbl2Str);