b5af24c744cd0f5571634a863c34f208bbd0a199 max Wed Sep 23 11:15:40 2026 -0700 hgTrackUi: better error when a faceted composite's metadata file is refused, refs #29344 The 400 error now says which URL was refused and for which track, and that a hub's metaDataUrl or colorSettingsUrl has to point inside the hub's directory. facetedComposite.js now shows that text instead of just "HTTP Status: 400". Also in facetedComposite.js: facet values with a count of zero are hidden, unless they are checked, and there is a new "Clear all filters" button that unchecks all facets and empties the search boxes. diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index bbb9878110f..d2d55bd1c84 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -4473,67 +4473,80 @@ /* Checks if a requested file is a legal request based on an attached cart or * native track. If so, retrieves the file content via UDC and retransmits * it as the page content. */ { char *genome = NULL; getDbAndGenome(cart, &database, &genome, NULL); initGenbankTableNames(database); char *fileUrl = cartOptionalString(cart, "fileUrl"); char *urlClone = cloneString(fileUrl); cgiDecode(urlClone, urlClone, strlen(urlClone)); fileUrl = resolveDotDots(urlClone); freeMem(urlClone); boolean matchFound = FALSE; +char *track = cartOptionalString(cart, "track"); // Check if fileUrl falls under a connected hub's base directory struct hubConnectStatus *hubStatusList = hubConnectStatusListFromCartAll(cart); struct hubConnectStatus *hubStatus = hubStatusList; while (hubStatus != NULL) { if (isEmpty(hubStatus->errorMessage) && fileUrlMatchesHub(fileUrl, hubStatus)) { matchFound = TRUE; break; } hubStatus = hubStatus->next; } // For native database tracks and curated hub tracks, check if fileUrl matches a // whitelisted trackDb setting. Only these are checked here because their settings are // admin-configured and trusted. User hub and custom track settings are user-controlled // and could be used for SSRF attacks. if (!matchFound) { - char *track = cartOptionalString(cart, "track"); char *sourceDb = cartOptionalString(cart, "sourceDb"); // for future quickLift use if (sourceDb == NULL) sourceDb = database; if (track != NULL && !isCustomTrack(track) && (!isHubTrack(track) || trackIsFromCuratedHub(sourceDb, track, hubStatusList))) { struct trackDb *tdb = tdbForTrack(sourceDb, track, NULL); if (tdb != NULL) matchFound = fileUrlMatchesTrackSetting(fileUrl, tdb); } } if (!matchFound) { + struct dyString *dy = dyStringNew(512); + dyStringPrintf(dy, "Requested URL '%s' does not fall under any connected hub's " + "directory, and does not match a whitelisted trackDb setting.", + fileUrl); + if (isNotEmpty(track)) + dyStringPrintf(dy, " This URL is the value of a fetchable trackDb setting " + "('metaDataUrl' or 'colorSettingsUrl') in the stanza for " + "track '%s'. Those settings name, respectively, the TSV " + "table of per-subtrack metadata and the JSON file of facet " + "colors used to build the filter/metadata table on this " + "page. For a track hub, such a URL must point to a file " + "inside the hub's own directory (alongside hub.txt), not an " + "external URL.", track); puts("Status: 400 Bad Request"); - errAbort("Supplied fileUrl does not match any connected hubs or track settings."); + errAbort("%s", dyStringContents(dy)); } // By now we know that fileUrl points to something valid to fetch and return to the user. // Now we just have to fetch the file contents and retransmit it. int timeout = cartUsualInt(cart, "udcTimeout", 300); if (udcCacheTimeout() < timeout) udcSetCacheTimeout(timeout); struct udcFile *udc = udcFileMayOpen(fileUrl, NULL); if (udc == NULL) { puts("Status: 404 Not Found"); cgiPrintContentType("text/plain"); printf("Error: could not open %s\n", fileUrl);