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/botDelay.c src/hg/lib/botDelay.c index 0b0f7fa85f2..c4c5ff2ffa6 100644 --- src/hg/lib/botDelay.c +++ src/hg/lib/botDelay.c @@ -282,30 +282,53 @@ if (e) *e = 0; if (sameString(remoteAddr, s)) found = TRUE; if (e) *e++ = ' '; s = e; } if (found) return TRUE; } } return FALSE; } +boolean botExceptionUserAgent() +/* Return TRUE if the HTTP_USER_AGENT matches a noCaptchaAgent. pattern in hg.conf. + * Mirrors cart.c's isUserAgentException(); kept here so non-cart callers + * (e.g. hubApi/blat.c) can reach it without pulling in the full cart library. */ +{ +char *agent = cgiUserAgent(); +if (!agent) + return FALSE; +struct slName *excStrs = cfgValsWithPrefix("noCaptchaAgent."); +if (!excStrs) + return FALSE; +struct slName *sl; +for (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; +} + int hgBotDelayTime() { return hgBotDelayTimeFrac(defaultDelayFrac); } int hgBotDelayTimeFrac(double fraction) /* Get suggested delay time from cgi using the standard penalty. */ { char *ip = getenv("REMOTE_ADDR"); char *host = cfgOption("bottleneck.host"); char *port = cfgOption("bottleneck.port"); int delay = 0; if (host != NULL && port != NULL && ip != NULL) {