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/dupTrack.c src/hg/lib/dupTrack.c index 9a2caf78f0e..ad69d5c1457 100644 --- src/hg/lib/dupTrack.c +++ src/hg/lib/dupTrack.c @@ -1,259 +1,269 @@ /* Stuff to help handle a track that is a duplicate of a another - sharing data * and type, but having it's own cart settings. */ #include "common.h" #include "hash.h" #include "cart.h" #include "dupTrack.h" #include "ra.h" #include "portable.h" #include "trashDir.h" #include "hgConfig.h" +static char *dupFileNameFromCart(struct cart *cart) +/* Return the name of the trash file holding the dupe track stanzas, or NULL. The value comes + * out of the cart, so only use it if it names a file the server itself made. */ +{ +char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL); +if (dupFileName != NULL && !isServerUserFilePath(dupFileName)) + return NULL; +return dupFileName; +} + boolean isDupTrack(char *track) /* determine if track name refers to a custom track */ { return (startsWith(DUP_TRACK_PREFIX, track)); } char *dupTrackSkipToSourceName(char *dupeTrackName) /* If it looks like it's a dupe track then skip over duppy part * in particular skip over dup_N_ form prefix for numerical N. */ { char *name = dupeTrackName; if ((name != NULL ) && startsWith(DUP_TRACK_PREFIX, name)) { char *s = name + strlen(DUP_TRACK_PREFIX); if (isdigit(s[0])) { s = skipNumeric(s); if (s[0] == '_') return s+1; } } return name; } static void makeDupName(char *sourceName, int n, char *buf, int bufSize) /* Create name for dupe */ { char *source = dupTrackSkipToSourceName(sourceName); safef(buf, bufSize, "%s%d_%s", DUP_TRACK_PREFIX, n, source); } static void findUniqDupName(char *sourceName, struct hash *dupHash, char nameBuf[], int nameBufSize) /* Make up a name of format dup_N_sourceTrack where N is a small unique number */ { int i; for (i=0; ;++i) { makeDupName(sourceName, i, nameBuf, nameBufSize); if (!hashLookup(dupHash, nameBuf)) return; } } char *dupTrackInCartAndTrash(char *sourceTrack, struct cart *cart, struct trackDb *sourceTdb) /* Update cart vars to reflect existance of duplicate of sourceTrack. * Also write out or append to dupe track trash file */ { -char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL); +char *dupFileName = dupFileNameFromCart(cart); FILE *f = NULL; // This will be our output /* We keep duplicate's name here. */ int bufSize = strlen(sourceTrack) + 32; char dupeTrackName[bufSize]; makeDupName(sourceTrack, 0, dupeTrackName, sizeof(dupeTrackName)); if (dupFileName != NULL && fileExists(dupFileName)) // Try and read in from old file { /* If there are already duplicates and trash cleaner hasn't nuked file read it in * and make sre we come up with a unique name before we append to existing file */ struct hash *dupHash = raReadAll(dupFileName, "track"); findUniqDupName(sourceTrack, dupHash, dupeTrackName, sizeof(dupeTrackName)); f = mustOpen(dupFileName, "a"); // Append to old file but use new name fputc('\n', f); } else { /* Otherwise we need to make up a new temp file and recored it in the cart */ struct tempName dupTn; trashDirDateFile(&dupTn, "dup", "dup", ".ra"); f = mustOpen(dupTn.forCgi, "w"); cartSetString(cart, DUP_TRACKS_VAR, dupTn.forCgi); } /* Write out new stanza */ fprintf(f, "track %s\n", dupeTrackName); fprintf(f, "source %s\n", dupTrackSkipToSourceName(sourceTrack)); int dupNo = atoi(dupeTrackName + strlen(DUP_TRACK_PREFIX)) + 1; if (sourceTdb != NULL) { fprintf(f, "shortLabel %s #%d\n", sourceTdb->shortLabel, dupNo); fprintf(f, "longLabel %s (duplicate #%d)\n", sourceTdb->longLabel, dupNo); } else { fprintf(f, "%s #%d\n", sourceTrack, dupNo); fprintf(f, "longLabel %s (duplicate #%d)\n", sourceTrack, dupNo); } carefulClose(&f); cartCloneVarsWithPrefix(cart, sourceTrack, dupeTrackName); return cloneString(dupeTrackName); } static void cartRemoveConfigVarsForTrack(struct cart *cart, char *track) /* Remove track, track.* and track_*. Perhaps this is more cautious than * track*? */ { cartRemove(cart, track); int nameSize = strlen(track); char namePlusBuf[nameSize+2]; strcpy(namePlusBuf, track); namePlusBuf[nameSize] = '.'; namePlusBuf[nameSize+1] = 0; cartRemovePrefix(cart, namePlusBuf); namePlusBuf[nameSize] = '_'; cartRemovePrefix(cart, namePlusBuf); } static void dupTracksRemoveAllFromCart(struct cart *cart) /* Remove all trace of dupe tracks from cart */ { cartRemovePrefix(cart, DUP_TRACK_PREFIX); } void undupTrackInCartAndTrash(char *dupName, struct cart *cart) /* Update cart vars to reflect removal of dupTrack. Also reduce or * remove trash file */ { -char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL); +char *dupFileName = dupFileNameFromCart(cart); if (dupFileName == NULL) return; // Nothing to do here /* We'll try and create our new list from info in old file */ struct dupTrack *newList = NULL; if (fileExists(dupFileName)) { struct dupTrack *dupList = dupTrackReadAll(dupFileName); /* Turn newList into a copy of dupList with our own stanza removed */ struct dupTrack *dup, *next; for (dup = dupList; dup != NULL; dup = next) { next = dup->next; if (!sameString(dupName, dup->name)) slAddHead(&newList, dup); } slReverse(&newList); } /* If we have any dupes left, write them to file, otherwise remove dup variable from cart */ if (newList == NULL) { dupTracksRemoveAllFromCart(cart); } else { FILE *f = mustOpen(dupFileName, "w"); struct dupTrack *dup; for (dup = newList; dup != NULL; dup = dup->next) { if (dup != newList) fputc('\n', f); struct slPair *tag; for (tag = dup->tagList; tag != NULL; tag = tag->next) fprintf(f, "%s\t%s\n", tag->name, (char *)(tag->val)); } carefulClose(&f); cartRemoveConfigVarsForTrack(cart, dupName); } } struct dupTrack *dupTrackReadAll(char *fileName) /* Read in ra file and return it as a list of dupTracks */ { struct dupTrack *list = NULL; struct slPair *tagList; struct lineFile *lf = lineFileMayOpen(fileName, TRUE); if (lf == NULL) return NULL; while ((tagList = raNextStanzAsPairs(lf)) != NULL) { char *name = slPairFindVal(tagList, "track"); if (name == NULL) errAbort("Trackless stanza ending line %d of %s", lf->lineIx, lf->fileName); char *source = slPairFindVal(tagList, "source"); if (source == NULL) errAbort("Sourceless stanza ending line %d of %s", lf->lineIx, lf->fileName); struct dupTrack *dup; AllocVar(dup); dup->name = cloneString(name); dup->source = cloneString(source); dup->tagList = tagList; slAddHead(&list, dup); } lineFileClose(&lf); slReverse(&list); return list; } static void dupTrackAddInfoToTdb(struct dupTrack *dup, struct trackDb *tdb) /* Add tags from dup to tdb */ { struct slPair *tag; for (tag = dup->tagList; tag != NULL; tag = tag->next) { if (!sameString(tag->name, "track")) { if (sameString(tag->name, "longLabel")) tdb->longLabel = tag->val; else if (sameString(tag->name, "shortLabel")) tdb->shortLabel = tag->val; hashAdd(tdb->settingsHash, tag->name, tag->val); } } } struct dupTrack *dupTrackListFromCart(struct cart *cart) /* Consult cart for dup track variable and if it's there return * list of dupes */ { -char *dupFileName = cartUsualString(cart, DUP_TRACKS_VAR, NULL); +char *dupFileName = dupFileNameFromCart(cart); if (dupFileName == NULL) return NULL; struct dupTrack *list = dupTrackReadAll(dupFileName); if (list == NULL) // Trash cleaner got it, so clean up cart too dupTracksRemoveAllFromCart(cart); return list; } struct dupTrack *dupTrackFindInList(struct dupTrack *list, char *name) /* Return matching element in list */ { struct dupTrack *dup; for (dup = list; dup != NULL; dup = dup->next) if (sameString(dup->name, name)) break; return dup; } struct trackDb *dupTdbFrom(struct trackDb *sourceTdb, struct dupTrack *dup) /* Generate a duplicate trackDb */ { /* Start with a basic shallow clone */ struct trackDb *tdb = CloneVar(sourceTdb); tdb->next = NULL; tdb->track = cloneString(dup->name); dupTrackAddInfoToTdb(dup, tdb); return tdb; } boolean dupTrackEnabled() /* Return true if we allow tracks to be duped. */ { return cfgOptionBooleanDefault("canDupTracks", TRUE); }