554396e44745165ee4baf5214aa771f2b17b3be6 braney Mon Aug 17 16:04:26 2026 -0700 hgPcr, cart: screen the PCR result file names read back out of the cart, refs #37623 The hgPcrResult_ cart variable holds two file names and an optional target name in one value. The cart.c arrays compare a whole value against isServerUserFilePath(), so none of them fit that shape. Add a fourth array for it and check the first two words. hgPcrResult_targetStyle shares the prefix and is a display setting, so it is excluded by name. Check both names where they are used as well, in pcrResultParseCart() and in hgPcr's append path, the way dupTrack.c already does. Two other things in writePcrResultTrack(). pcrFiles[2] was read without ever being set whenever the value held only two words, which is the usual case. And the saved-session test was a plain prefix compare that missed sessionDataDirOld; it now asks whether the file is in the trash instead. hg/utils/cartFileVarCatalog knows about the new array and has a row for hgPcrResult_ saying why its scan cannot see this one. diff --git src/hg/utils/cartFileVarCatalog/harvestCartFileVars.py src/hg/utils/cartFileVarCatalog/harvestCartFileVars.py index ccbb4d4bce3..0afc6850158 100755 --- src/hg/utils/cartFileVarCatalog/harvestCartFileVars.py +++ src/hg/utils/cartFileVarCatalog/harvestCartFileVars.py @@ -63,36 +63,38 @@ # instead of somebody's working tree, where a stray .c file or a half-finished # edit would show up as a finding. ROOT = os.environ.get("KENT_SRC") or os.path.expanduser("~/kent/src") # Walked for call sites. hg/lib is in here because most of the sinks are there # (customTrack.c, dupTrack.c, trackHub.c) rather than in any one CGI. SCAN_ROOTS = ["hg", "lib"] # Not source we care about, and walking them is slow. SKIP_DIRS = {"htdocs", "js", "tests", "expected", "input", "trackDb", "makeDb/doc", "CVS", ".git", "python", "lowelab"} # Mined for #define values in addition to everything under SCAN_ROOTS. MACRO_DIRS = ["inc", "hg/inc"] -# Where the screening list lives, and the three arrays in it. The third holds +# Where the screening list lives, and the four arrays in it. The third holds # the names that may legitimately be a remote URL instead of a file, which are -# screened with isServerUserFileOrUrl() rather than isServerUserFilePath(). +# screened with isServerUserFileOrUrl() rather than isServerUserFilePath(). The +# fourth holds the ones whose value is two file names and a trailing word rather +# than one file name; cart.c checks the two names. CART_C = os.path.join("hg", "lib", "cart.c") SCREEN_ARRAYS = ("fileNameCartVars", "fileNameCartVarPrefixes", - "urlOrFileNameCartVars") + "urlOrFileNameCartVars", "fileNamePairCartVarPrefixes") # --------------------------------------------------------------------------- # macro table # --------------------------------------------------------------------------- def macro_files(): """Every .c and .h worth reading a #define out of.""" seen = set() for root in list(SCAN_ROOTS) + MACRO_DIRS: base = os.path.join(ROOT, root) for dirpath, dirnames, filenames in os.walk(base): rel = os.path.relpath(dirpath, ROOT) if any(part in SKIP_DIRS for part in rel.split(os.sep)): dirnames[:] = [] @@ -390,52 +392,54 @@ # An entry may concatenate: customCompositeCartName "-" pieces = re.findall(r'"(?:[^"\\]|\\.)*"|[A-Za-z_]\w*', entry) val = "" for piece in pieces: got = resolve(piece, macro, localconst, conflict) if got is None or got.startswith("{"): val = None break val += got if val: names.add(val) out.append(names) return tuple(out) -def screened(name, names, prefixes, urlOrFile=None): - """Does cart.c check this cart variable on the way in, by any of the three?""" +def screened(name, names, prefixes, urlOrFile=None, pairPrefixes=None): + """Does cart.c check this cart variable on the way in, by any of the four?""" if names is None or prefixes is None: return False if name in names or name in (urlOrFile or ()): return True - return any(name.startswith(p) for p in prefixes) + return any(name.startswith(p) + for p in list(prefixes) + list(pairPrefixes or ())) # --------------------------------------------------------------------------- # reporting # --------------------------------------------------------------------------- def harvest(): macro, conflict = build_macros() flows, suspects = scan(macro, conflict) - names, prefixes, urlOrFile = read_screen(macro, conflict) + names, prefixes, urlOrFile, pairPrefixes = read_screen(macro, conflict) srt = lambda s: sorted(s) if s is not None else None return dict(flows=flows, suspects={k: sorted(v) for k, v in suspects.items()}, screenNames=srt(names), screenPrefixes=srt(prefixes), - screenUrlOrFile=srt(urlOrFile)) + screenUrlOrFile=srt(urlOrFile), + screenPairPrefixes=srt(pairPrefixes)) def by_name(flows): out = collections.defaultdict(list) for f in flows: out[f["name"]].append(f) return out def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--flows", action="store_true", help="every value-to-file-call flow, grouped by cart name") ap.add_argument("--names", action="store_true", @@ -457,51 +461,57 @@ return 0 if args.screen: if h["screenNames"] is None: print("no fileNameCartVars[] in %s" % CART_C) return 1 print("fileNameCartVars (%d)" % len(h["screenNames"])) for n in h["screenNames"]: print(" %s" % n) print("fileNameCartVarPrefixes (%d)" % len(h["screenPrefixes"])) for n in h["screenPrefixes"]: print(" %s" % n) print("urlOrFileNameCartVars (%d)" % len(h["screenUrlOrFile"] or [])) for n in h["screenUrlOrFile"] or []: print(" %s" % n) + print("fileNamePairCartVarPrefixes (%d)" + % len(h["screenPairPrefixes"] or [])) + for n in h["screenPairPrefixes"] or []: + print(" %s" % n) return 0 if args.names: for n in sorted(groups): print(n) return 0 if args.suspects: for n in sorted(h["suspects"]): mark = " (flow)" if n in groups else "" print("%s%s" % (n, mark)) for site in h["suspects"][n]: print(" %s" % site) return 0 if args.flows: names = set(h["screenNames"] or []) prefixes = set(h["screenPrefixes"] or []) urlOrFile = set(h["screenUrlOrFile"] or []) + pairPrefixes = set(h["screenPairPrefixes"] or []) for n in sorted(groups): - mark = ("screened" if screened(n, names, prefixes, urlOrFile) + mark = ("screened" if screened(n, names, prefixes, urlOrFile, + pairPrefixes) else "NOT screened") print("%s [%s]" % (n, mark)) for f in groups[n]: print(" %s:%d %s(%s)" % (f["file"], f["line"], f["sink"], f["local"])) return 0 print("cart variables with a value reaching a file call %d" % len(groups)) print("flows %d" % len(flows)) print("file-ish cart names (--suspects) %d" % len(h["suspects"])) if h["screenNames"] is None: print("cart.c screening list absent") else: print("cart.c screening list %d names, "