905b9cb05eeaca7f2dcda42fc6abdb95a2d2da7f
max
  Wed Sep 9 05:29:28 2026 -0700
no captcha for a command-line CGI run, and version the detailsScript module URL

Two small fixes to things noticed while adding the scatterPlot plot type.

A CGI run from the command line got the Cloudflare Turnstile challenge page
instead of the output the caller asked for, which makes "./hgc db=hg38 g=x" -
the quickest way to see what a CGI emits - useless without a hand-made hg.conf.
There is no browser to solve a captcha in that situation. printCaptcha() now
returns early when cgiWasSpoofed(). That flag cannot be set from an HTTP
request: cgiFromCommandLine() returns early and leaves it FALSE whenever the
web server has set REQUEST_METHOD. Checked that a plain argument-style run is
now clean, that a run which fakes the web environment with QUERY_STRING still
gets the captcha, and that an HTTP request behaves exactly as the unmodified
binary does.

The detailsScript module was loaded from a hardcoded import('../js/hgc.X.js'),
bypassing webTimeStampedLinkToResource(), so it was the one script on the page
with no ?v=<mtime>. That is the mechanism that flushes a browser's cache when
the CGI version changes and that keeps a mirror from pairing an old static file
with new CGIs, and without it a cached module could be handed newer bedDetails
JSON than it was written for. Now built through the helper, which also fixes the
already-shipped histogram type. The helper errAborts on a missing file and the
plot type comes from a hub, so a plot type with no module installed falls back to
the plain path: a silent failed import as before, rather than one bad hub setting
taking down the whole details page.

refs #35415

diff --git src/hg/lib/cart.c src/hg/lib/cart.c
index 7a6035e03ac..e6a36af1074 100644
--- src/hg/lib/cart.c
+++ src/hg/lib/cart.c
@@ -1687,30 +1687,38 @@
     return res;
 }
 
 // hg.conf key with the cloud flare secret key, used twice here, so a global macro
 #define CLOUDFLARESITEKEY "cloudFlareSiteKey"
 
 static char *getSessionId()
 /* Get session id if any from CGI. */
 {
 return cgiOptionalString("hgsid");
 }
 
 void printCaptcha() 
 /* print an html page that shows the captcha and on success, reloads the page with the token added as token=x */
 {
+    // A CGI run from the command line has no browser to solve a captcha, so the
+    // challenge page would just replace the output the caller asked for. Only a
+    // real command-line run reaches here with wasSpoofed set: cgiFromCommandLine()
+    // returns early, leaving it FALSE, whenever the web server has set
+    // REQUEST_METHOD, so this cannot be reached from an HTTP request.
+    if (cgiWasSpoofed())
+        return;
+
     char *cfSiteKey = cfgVal(CLOUDFLARESITEKEY);
     if (!cfSiteKey)
         return;
 
     if (cfgOptionBooleanDefault("captchaDebug", FALSE))
         fprintf(stderr, "CAPTCHA_PRINT %s\n", getSessionId());
     cspWriteResponseHeader();
     puts("Content-Type:text/html\n"); // puts outputs one newline. Header requires two newlines.
     puts("<html><head>");
     printf("<script nonce='%s'>\n", getNonce());
     printf("function showWidget() { \n"
        "turnstile.render('#myWidget', {\n"
          "sitekey: '%s',\n"
          "theme: 'light',\n"
          "callback: function (token) {\n"