e6ddf60465deb96e43be6738c5ca6a7a6168cac8
braney
  Sat Aug 22 15:08:59 2026 -0700
hg/lib: add an option to send the content policy as an http response header

New cspWriteResponseHeader() in hg/lib/hCommon.c, gated on hg.conf's
cspResponseHeader, which defaults off everywhere.  The policy string itself is
still built by the existing code in lib/htmshell.c, which now also knows how to
format it as a response header.  Both carry the same nonce, since getNonce() is
one per process, so a page may safely have the header and the meta tag.

Most pages pick it up from addHttpHeaders() in cart.c, the existing hook for
extra response headers, which every cart based CGI already passes through.  Six
places build their own http header block and so call it directly: the two "too
many requests" pages, the captcha and its error page, the hubApi help redirect,
and the hgSearch redirect to hgTracks.

Inline scripts on three of those pages now carry the nonce, and the policy
allows the Cloudflare script the bot check loads, so the option works when it
is turned on.  The Cloudflare entry is the only part of this that takes effect
with the option off.

diff --git src/hg/hubApi/hubApi.c src/hg/hubApi/hubApi.c
index 979e8fa8725..ad680bc4da0 100644
--- src/hg/hubApi/hubApi.c
+++ src/hg/hubApi/hubApi.c
@@ -1,18 +1,19 @@
 /* hubApi - access mechanism to hub data resources. */
 #include "dataApi.h"
 #include "botDelay.h"
+#include "hCommon.h"
 #include "jsHelper.h"
 #include "srcVersion.h"
 #include "asmAlias.h"
 #include "hubSpaceKeys.h"
 /* can not include bamFile.h with the liftOver business, there
  * is a conflict in a definition of the enum 'bed'
  */
 #include "bamFile.h"
 
 /*
 +------------------+------------------+------+-----+---------+-------+
 | Field            | Type             | Null | Key | Default | Extra |
 +------------------+------------------+------+-----+---------+-------+
 | hubUrl           | longblob         | NO   | PRI | NULL    |       |
 | shortLabel       | varchar(255)     | NO   |     | NULL    |       |
@@ -1149,30 +1150,31 @@
 hPrintf("</pre>\n");
 }
 
 static void sendJsonHogMessage(char *hogHost)
 {
 apiErrAbort(err429, err429Msg, "Your host, %s, has been sending too many requests lately and is "
        "unfairly loading our site, impacting performance for other users. "
        "Please contact genome-www@soe.ucsc.edu to ask that your site "
        "be reenabled.  Also, please consider downloading sequence and/or "
        "annotations in bulk -- see http://genome.ucsc.edu/downloads.html.",
        hogHost);
 }
 
 static void sendHogMessage(char *hogHost)
 {
+cspWriteResponseHeader();
 puts("Content-Type:text/html");
 hPrintf("Status: %d %s\n", err429, err429Msg);
 puts("Retry-After: 30");
 puts("\n");
 
 hPrintf("<!DOCTYPE HTML>\n");
 hPrintf("<html lang='en'>\n");
 hPrintf("<head>\n");
 hPrintf("<meta charset=\"utf-8\">\n");
 hPrintf("<title>Status %d %s</title></head>\n", err429, err429Msg);
 
 hPrintf("<body><h1>Status %d %s</h1><p>\n", err429, err429Msg);
 hPrintf("Your host, %s, has been sending too many requests lately and is "
        "unfairly loading our site, impacting performance for other users. "
        "Please contact genome-www@soe.ucsc.edu to ask that your site "
@@ -1658,30 +1660,31 @@
 	if (n == -1)	/* can use -1 to indicate as much as allowed */
 	    maxItemsOutput = maxItemLimit;
 	else if (n > maxItemLimit)	/* safety check */
 	    apiErrAbort(err400, err400Msg, "requested maxItemsOutput '%s' greater than maximum limit allowed: %d", maxOut, maxItemLimit);
 	else if (n < 1)
 	    apiErrAbort(err400, err400Msg, "requested maxItemsOutput '%s' can not be less than one", maxOut, maxItemLimit);
 	else
 	    maxItemsOutput = n;
 	}
     }
 }	/*	static void setGlobalCgiVars()	*/
 
 static void redirectToHelp()
 /* redirect to the help page */
 {
+cspWriteResponseHeader();
 puts("Content-Type:text/html");
 hPrintf("Status: %d %s\n", err301, err301Msg);
 hPrintf("Location: /goldenPath/help/api.html\n");
 puts("\n");
 
 hPrintf("<!DOCTYPE HTML>\n");
 hPrintf("<html lang='en'>\n");
 hPrintf("<head>\n");
 hPrintf("<meta http-equiv='Refresh' content='0; url=/goldenPath/help/api.html' />\n");
 hPrintf("</head>\n");
 }
 
 /* Null terminated list of CGI Variables we don't want to save
  * permanently. */
 static char *excludeVars[] = {"Submit", "submit", "sourceSelected", "selectRadio", "ucscGenome", "publicHubs", "clade", NULL,};