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/blatShare.c src/hg/lib/blatShare.c index 1908f09aacf..73aa37e8e4a 100644 --- src/hg/lib/blatShare.c +++ src/hg/lib/blatShare.c @@ -1,114 +1,111 @@ /* blatShare.c - reopen durable BLAT bigPsl results. Shared by hgBlat (which rebuilds the results * table for a shared "?u=&s=" link) and hgc (which rebuilds one base-by-base alignment for a shared * alignment link), so both read the durable custom track through the same code and the same * server-file security check. */ /* Copyright (C) 2024 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "localmem.h" #include "psl.h" #include "bbiFile.h" #include "bigBed.h" #include "bigPsl.h" #include "cart.h" #include "chromAlias.h" #include "hgConfig.h" #include "portable.h" +#include "trashDir.h" #include "blatShare.h" struct psl *pslListFromBigPslFile(char *bbFileName) /* Read every alignment out of a bigPsl bigBed file into a psl list, in query-display order. * Used to rebuild the Table view for a shared link from the durable custom track. */ { struct bbiFile *bbi = bigBedFileOpenAlias(bbFileName, chromAliasFindAliases); struct lm *lm = lmInit(0); struct psl *pslList = NULL; struct bbiChromInfo *chrom, *chromList = bbiChromList(bbi); for (chrom = chromList; chrom != NULL; chrom = chrom->next) { struct bigBedInterval *bb, *ivList = bigBedIntervalQuery(bbi, chrom->name, 0, chrom->size, 0, lm); for (bb = ivList; bb != NULL; bb = bb->next) { /* pslFromBigPsl ignores its seqTypeField arg (it reads seqType from the record itself). */ struct psl *psl = pslFromBigPsl(chrom->name, bb, 0, NULL, NULL); /* pslFromBigPsl always yields a two-char (query,target) strand with the target normalized * to '+'; a DNA BLAT is shown with a single-char strand, so drop the redundant target '+' * for non-protein queries to match the classic/fresh result page. */ if (!pslIsProtein(psl) && psl->strand[1] == '+') psl->strand[1] = 0; slAddHead(&pslList, psl); } } bbiChromInfoFreeList(&chromList); lmCleanup(&lm); bbiFileClose(&bbi); return pslList; } struct psl *pslFromBigPslFileMatch(char *bbFileName, char *chrom, int tStart, char *qName, char **retSeq, char **retCds) /* Return the single alignment in a bigPsl bigBed matching chrom:tStart and qName, together with its * stored query sequence (retSeq) and CDS (retCds) when those out pointers are non-NULL, or NULL if * there is no such alignment. Caller frees the returned psl (and any returned seq and cds). */ { struct bbiFile *bbi = bigBedFileOpenAlias(bbFileName, chromAliasFindAliases); struct lm *lm = lmInit(0); struct psl *result = NULL; if (retSeq != NULL) *retSeq = NULL; if (retCds != NULL) *retCds = NULL; /* Every record overlapping [tStart, tStart+1) is returned; keep the one that actually starts at * tStart and carries the requested query name. */ struct bigBedInterval *bb, *ivList = bigBedIntervalQuery(bbi, chrom, tStart, tStart + 1, 0, lm); for (bb = ivList; bb != NULL; bb = bb->next) { if (bb->start != tStart) continue; char *seq = NULL, *cds = NULL; struct psl *psl = pslFromBigPsl(chrom, bb, 0, &seq, &cds); if (sameString(psl->qName, qName)) { if (!pslIsProtein(psl) && psl->strand[1] == '+') psl->strand[1] = 0; result = psl; if (retSeq != NULL) *retSeq = seq; else freeMem(seq); if (retCds != NULL) *retCds = cds; else freeMem(cds); break; } pslFree(&psl); freeMem(seq); freeMem(cds); } lmCleanup(&lm); bbiFileClose(&bbi); return result; } char *blatFindPinnedBigPsl(struct cart *cart) /* Return a cloned path to the BLAT bigPsl bigBed that hgc's buildBigPsl pinned in the cart * (blatLastBigBed), or NULL. Reading the pinned value is unambiguous even when the cart holds * several BLAT custom tracks from earlier searches. A shared session (?u=&s=) can carry an arbitrary * value here, so only ever accept a local file the server itself placed under its trash dir, or under * the durable session-data dir (where a saved session's trash files are moved) - never a remote URL * or an arbitrary local path. This is the trash/sessionData allow-list from isValidBigDataUrl(). */ { char *f = cartOptionalString(cart, "blatLastBigBed"); if (f == NULL) return NULL; -char *sessionDataDir = cfgOption("sessionDataDir"); -char *sessionDataDirOld = cfgOption("sessionDataDirOld"); -if (startsWith(trashDir(), f) || - (isNotEmpty(sessionDataDir) && startsWith(sessionDataDir, f)) || - (isNotEmpty(sessionDataDirOld) && startsWith(sessionDataDirOld, f))) +if (isTrashOrSessionDataPath(f)) return cloneString(f); return NULL; }