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/lib/customTrack.c src/hg/lib/customTrack.c index fd4f8f563cf..e24a910f6cc 100644 --- src/hg/lib/customTrack.c +++ src/hg/lib/customTrack.c @@ -955,30 +955,36 @@ errCatch->message->string); loadFailed = TRUE; } errCatchFree(&errCatch); // If there was a failure in loading the custom tracks, return immediately -- don't try to // add or merge in new custom tracks. The cartRemove statements below will be skipped, so we // can try again next click. if (loadFailed) return NULL; } /* Layer in the user's myVariants tracks from mvCtfile_. */ char mvVar[256]; safef(mvVar, sizeof mvVar, MYVARIANTS_FILE_VAR_PREFIX "%s", genomeDb); char *mvFile = cartOptionalString(cart, mvVar); +if (isNotEmpty(mvFile) && !isServerUserFilePath(mvFile)) + { + /* not a file we made; drop the pointer rather than parse it */ + cartRemove(cart, mvVar); + mvFile = NULL; + } if (isNotEmpty(mvFile) && fileExists(mvFile)) { struct customTrack *mvList = NULL; struct errCatch *mvCatch = errCatchNew(); if (errCatchStart(mvCatch)) mvList = customFactoryParse(genomeDb, mvFile, TRUE, fileName, NULL); errCatchEnd(mvCatch); if (mvCatch->gotError) { if (isNotEmpty(mvCatch->message->string)) warn("myVariants load error: %s", mvCatch->message->string); cartRemove(cart, mvVar); mvList = NULL; } errCatchFree(&mvCatch); @@ -1111,37 +1117,37 @@ struct customTrack *ctList = customTracksParseCartDetailed(genomeDb, cart, retBrowserLines, retCtFileName, NULL, NULL, &err, NULL); if (err) warn("%s", err); return ctList; } boolean customTracksExistDb(struct cart *cart, char *db, char **retCtFileName) /* determine if there are any custom tracks for db. Cleanup from expired tracks */ { char *ctFileVar = customTrackFileVar(db); char *ctFileName = cartOptionalString(cart, ctFileVar); if (ctFileName) { - if (fileExists(ctFileName)) + if (isServerUserFilePath(ctFileName) && fileExists(ctFileName)) { if (retCtFileName) *retCtFileName = ctFileName; return TRUE; } - /* expired custom tracks file */ + /* expired custom tracks file, or not a file we made */ cartRemove(cart, ctFileVar); cartRemovePrefix(cart, CT_PREFIX); } return FALSE; } boolean customTracksExist(struct cart *cart, char **retCtFileName) /* determine if there are any custom tracks. Cleanup from expired tracks */ { return customTracksExistDb(cart, cartString(cart, "db"), retCtFileName); } boolean isCustomTrack(char *track) /* determine if track name refers to a custom track */ {