9ad04e0a0b06ec3c4f09ef1b6c3ce6be79b61c68
braney
  Sun Aug 16 11:56:56 2026 -0700
cart: validate file names read back out of the cart

Several cart variables hold the name of a file the server created for a user.
Route them through one shared check, isServerUserFilePath(), which accepts the
trash directory, the session-data directories and myVariantsDataDir, and apply
it both where values enter the cart and where the file names are used.

A few of these variables may instead hold a remote URL.  Those get their own
list and isServerUserFileOrUrl(), because the code that reads them chooses
between a fetch and a local open by looking for a protocol.

Consolidates two hand-rolled copies of the same test in blatShare.c and
customFactory.c, and drops the weaker private copy in sessionData.c.

Adds hg/utils/cartFileVarCatalog, a registry that scans the tree for a cart
value reaching a file call and reconciles what it finds against the lists in
cart.c, so a new one of these cannot be added without somebody noticing.  Its
--reconcile is quiet enough for the nightly cron the other catalogs use, and it
is what turned up seven of the names now on those lists.

refs #37623

diff --git src/hg/hgIntegrator/hgIntegrator.c src/hg/hgIntegrator/hgIntegrator.c
index 6eee46f4754..458c6ab044b 100644
--- src/hg/hgIntegrator/hgIntegrator.c
+++ src/hg/hgIntegrator/hgIntegrator.c
@@ -19,30 +19,31 @@
 #include "cartTrackDb.h"
 #include "cheapcgi.h"
 #include "htmshell.h"
 #include "genbank.h"
 #include "hAnno.h"
 #include "hCommon.h"
 #include "hdb.h"
 #include "hgColors.h"
 #include "hui.h"
 #include "joiner.h"
 #include "jsHelper.h"
 #include "jsonParse.h"
 #include "knetUdc.h"
 #include "textOut.h"
 #include "trackHub.h"
+#include "trashDir.h"
 #include "userRegions.h"
 #include "web.h"
 #include "annoFormatTab.h"
 #include "annoGratorQuery.h"
 #include "windowsToAscii.h"
 #include "chromAlias.h"
 #include "hgConfig.h"
 
 /* Global Variables */
 struct cart *cart = NULL;             /* CGI and other variables */
 struct hash *oldVars = NULL;          /* Old contents of cart before it was updated by CGI */
 
 #define QUERY_SPEC "hgi_querySpec"
 #define UI_CHOICES "hgi_uiChoices"
 #define DO_QUERY "hgi_doQuery"
@@ -410,31 +411,32 @@
 
 // For now at least, use hgTables' CGI var names so regions are shared between hgI & hgTables
 //#*** TODO: get own CGI var names or libify these (dup'd from hgTables.h)
 #define hgtaEnteredUserRegions "hgta_enteredUserRegions"
 #define hgtaUserRegionsFile "hgta_userRegionsFile"
 #define hgtaUserRegionsDb "hgta_userRegionsDb"
 #define hgtaRegionTypeUserRegions "userRegions"
 #define hgtaRegionTypeGenome "genome"
 
 
 boolean userRegionsExist()
 /* Return true if the trash file for regions exists.  It must be non-empty because
  * if the region list is set to empty we clear region state. */
 {
 char *trashFileName = cartOptionalString(cart, hgtaUserRegionsFile);
-return (isNotEmpty(trashFileName) && fileExists(trashFileName));
+return (isNotEmpty(trashFileName) && isServerUserFilePath(trashFileName) &&
+        fileExists(trashFileName));
 }
 
 struct bed4 *userRegionsGetBedList()
 /* Read parsed user-defined regions from local trash file and return as bed list. */
 // Not libifying at this point because the cart variable names may differ between
 // apps -- in that case, libify this but with some kind of param to give cart
 // var name prefix.
 {
 if (! userRegionsExist())
     return NULL;
 char *trashFileName = cartOptionalString(cart, hgtaUserRegionsFile);
 // Note: I wanted to use basicBed's bedLoadNAll but it chops by whitespace not tabs,
 // so it aborts if the name field is empty (that causes it to see 3 words not 4).
 char *words[4];
 int wordCount;
@@ -499,31 +501,31 @@
     }
 }
 
 static void clearUserRegions(struct cartJson *cj, struct hash *paramHash)
 /* Remove all user-defined region info from cart, and send JSON update. */
 // Not libifying at this point because the cart variable names may differ between
 // apps -- in that case, libify this but with some kind of param to give cart
 // var name prefix.
 {
 char *resultName = cartJsonOptionalParam(paramHash, "resultName");
 if (isEmpty(resultName))
     resultName = "userRegions";
 struct jsonWrite *jw = cj->jw;
 cartRemove(cart, hgtaUserRegionsDb);
 char *trashFileName = cartOptionalString(cart, hgtaUserRegionsFile);
-if (trashFileName && fileExists(trashFileName))
+if (trashFileName && isServerUserFilePath(trashFileName) && fileExists(trashFileName))
     unlink(trashFileName);
 cartRemove(cart, hgtaUserRegionsFile);
 cartRemove(cart, hgtaEnteredUserRegions);
 char *regionType = cartUsualString(cart, hgiRegionType, hgiRegionTypeDefault);
 if (regionType && sameString(regionType, hgtaRegionTypeUserRegions))
     {
     regionType = hgiRegionTypeDefault;
     cartSetString(cart, hgiRegionType, regionType);
     }
 jsonWriteString(jw, hgiRegionType, regionType);
 jsonWriteString(jw, resultName, NULL);
 jsonWriteString(jw, "userRegionsSummary", NULL);
 }
 
 static void setUserRegions(struct cartJson *cj, struct hash *paramHash)