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("' in hgc.c:printEmbeddedTable()"); swapped = strSwapStrs(table, 4096, "|", "\n", table); printf("
"); char table[4096]; safef(table, sizeof(table), "%s", thisTbl->encodedTbl); int swapped = strSwapStrs(table, 4096, ";", "
"); if (swapped == -1) errAbort("Error substituting ';' for '
"); if (swapped == -1) errAbort("Error substituting '|' for '' in hgc.c:printEmbeddedTable()"); printf("%s
\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("%s\n", fields[ix]); printCount++; } if (skipIds) slFreeList(skipIds); if (sepFields) slFreeList(sepFields); if (embeddedTblFields) { printf("
\n"); struct embeddedTbl *thisTbl; struct dyString *tableLabelsDy = dyStringNew(0); + boolean initted = FALSE; for (thisTbl = embeddedTblList; thisTbl != NULL; thisTbl = thisTbl->next) { if (thisTbl->encodedTbl) { + if (!initted) + { + initted = TRUE; dyStringPrintf(tableLabelsDy, "var _jsonHgcLabels = ["); + } printEmbeddedTable(tdb, thisTbl, tableLabelsDy); - dyStringPrintf(tableLabelsDy, "];\n"); } } + if (initted) + dyStringPrintf(tableLabelsDy, "];\n"); jsInline(dyStringCannibalize(&tableLabelsDy)); } if (printCount > 0) printf("
\n"); if (detailsTableFields) { printExtraDetailsTable(tdb->track, extraDetailsTableName, extraDetails, extraTblStr); } if (detailsTable2Fields) { printExtraDetailsTable(tdb->track, extraDetails2TableName, extraDetails2, extraTbl2Str);