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/lib/hCommon.c src/hg/lib/hCommon.c index ee6c0a2fe35..41e865de9e5 100644 --- src/hg/lib/hCommon.c +++ src/hg/lib/hCommon.c @@ -1,26 +1,27 @@ /* hCommon.c - routines used by many files in hgap project. */ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "hCommon.h" #include "chromInfo.h" #include "portable.h" #include "hgConfig.h" #include "errAbort.h" +#include "htmshell.h" static char *_hgcName = "../cgi-bin/hgc"; /* Path to click processing program. */ static char *_hgTracksName = "../cgi-bin/hgTracks"; /* Path back to genome browser. */ static char *_hgTrackUiName = "../cgi-bin/hgTrackUi"; /* Path to extended ui program. */ static char *_hgFileUiName = "../cgi-bin/hgFileUi"; /* Path to downloladable files CGI. */ static char *_hgTextName = "../cgi-bin/hgText"; /* Path back to the text browser. */ static char *_hgTablesName = "../cgi-bin/hgTables"; /* Path back to the table browser. */ static char *_hgVaiName = "../cgi-bin/hgVai"; /* Path back to the variant annotation integrator. */ static char *_hgCustomName = "../cgi-bin/hgCustom"; /* Path back to the custom tracks manager. */ static char *_hgCollectionName = "../cgi-bin/hgCollection"; /* Path back to the composite builder */ static char *_hgHubConnectName = "../cgi-bin/hgHubConnect"; /* Path back to the track hub manager. */ static char *_hgSessionName = "../cgi-bin/hgSession"; /* Path to session manager. */ static char *_hgPalName = "../cgi-bin/hgPal"; /* Path back to the protein aligner */ static char *_hgVarAnnogratorName = "../cgi-bin/hgVarAnnogrator"; /* Path to variant annot intgr */ @@ -406,15 +407,33 @@ void hUserAbort(char *format, ...) /* errAbort when a `user' error is detected. This is an error that comes * from user input. This disables the logging stack dumps. */ { va_list args; va_start(args, format); hVaUserAbort(format, args); va_end(args); } boolean hAllowAllTables(void) /* Return TRUE if hg.conf's hgta.disableAllTables doesn't forbid an 'all tables' menu. */ { return !cfgOptionBooleanDefault("hgta.disableAllTables", FALSE); } + +void cspWriteResponseHeader(void) +/* Write the Content Security Policy as an http response header, if hg.conf + * turns it on. Must be called before the blank line that ends the http header + * block. Only the first call in a process writes anything. + * + * This exists so that pages which build their own http header block, and so + * never reach the library code that writes the meta tag, still carry a policy. + * The gate is off by default so it can be turned on one machine at a time. */ +{ +static boolean written = FALSE; +if (written) + return; +if (!cfgOptionBooleanDefault("cspResponseHeader", FALSE)) + return; +written = TRUE; +generateCspResponseHeader(stdout); +}