2197f6d5208aff4c48ccbe42e61a116d988ac392 max Tue May 19 08:23:54 2026 -0700 hubApi: add /blat endpoint with apiKey gating, format=hgblat, and known-agent bypass New src/hg/hubApi/blat.c implements /blat/ (dna, protein, transRna, transDna, guess) backed by the same gfServer logic as hgBlat. Key details: - Requires an apiKey for rate-limiting; botException() and botExceptionUserAgent() exempt IPs/user-agents in hg.conf (same policy as captcha bypass elsewhere in the browser stack). - Invalid apiKey returns a clean JSON 403 rather than an HTML 500 (pre-validated in hubApi.c main() before hgBotDelayTimeFrac runs). - Extra bot-delay fraction (default 0.3, 10x hubApi default) is configurable via hubApi.blatDelayFraction in hg.conf. - format=text/psl -> PSL text; format=hgblat -> byte-for-byte hgBlat?output=json shape; jsonOutputArrays=1 -> hubApi envelope with arrays (parallel to getData behaviour); default -> objects. - botExceptionUserAgent() carved out of cart.c's static isUserAgentException() into botDelay.c so non-cart callers can use it. - Cross-reference comments added in hgBlat.c and blat.c noting the shared logic so fixes get applied to both. refs #36315 Co-Authored-By: Claude Sonnet 4.6 diff --git src/hg/lib/cart.c src/hg/lib/cart.c index 99340caa55b..f37ac597263 100644 --- src/hg/lib/cart.c +++ src/hg/lib/cart.c @@ -1539,49 +1539,31 @@ puts(""); puts("\n"); puts("

The Genome Browser is protecting itself from bots. This will just take a few seconds.

"); puts("To make programmatic queries, see our FAQ: https://genome.ucsc.edu/FAQ/FAQdownloads.html#CAPTCHA."); puts(""); puts(""); puts("
"); puts(""); sqlCleanupAll(); // we are wondering about hanging connections, so just in case, close them. exit(0); } static boolean isUserAgentException() /* return true if HTTP user-agent is in list of exceptions in hg.conf */ { -char *agent = cgiUserAgent(); -if (!agent) - return FALSE; - -struct slName *excStrs = cfgValsWithPrefix("noCaptchaAgent."); -if (!excStrs) - return FALSE; - -struct excReStr; -for (struct slName *sl = excStrs; sl != NULL; sl = sl->next) - { - if (regexMatch(agent, sl->name)) - { - fprintf(stderr, "CAPTCHAPASS %s matches %s\n", agent, sl->name); - return TRUE; - } - } - -return FALSE; +return botExceptionUserAgent(); } void forceUserIdOrCaptcha(struct cart* cart, char *userId, boolean userIdFound, boolean fromCommandLine) /* print captcha if user did not sent a valid hguid cookie or a valid * cloudflare token. Allow certain IPs and user-agents. */ { static boolean captchaCheckDone = FALSE; // No need to do this again. Can happen if cartNew() is called somewhere else in a CGI if (captchaCheckDone) return; captchaCheckDone = TRUE; if (fromCommandLine || isEmpty(cfgOption(CLOUDFLARESITEKEY)))