Skip to content

fix(middleware): make crawler detection work and isolate bot traffic - #949

Draft
thostetler wants to merge 2 commits into
adsabs:masterfrom
thostetler:fix/bot-detection-ua-match
Draft

thostetler wants to merge 2 commits into
adsabs:masterfrom
thostetler:fix/bot-detection-ua-match

Conversation

@thostetler

Copy link
Copy Markdown
Member

Crawler user agents were matched by exact map lookup against the whole UA string, which was probably making some bots not match

  • Fixes the existing IP checks and improves the reverse DNS check
  • Adds a better way to classify bots as much as we can using our known list of UAs

Crawler user agents were matched by exact map lookup against the whole UA string, so no real crawler ever matched. Fixing that exposed three more defects: IP checks compared bare addresses instead of CIDR ranges, reverse DNS accepted any host with a PTR record, and classification was discarded by a second iron-session write.

Verified-tier classification now requires TRUSTED_CLIENT_IP_HEADER, since X-Forwarded-For's leftmost entry is client-supplied. Without that header, matched crawlers are capped at the unverifiable tier instead of getting an elevated-quota token.
@thostetler
thostetler requested a review from shinyichen October 5, 2026 22:57
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown

Bundle size

Shared by all pages: 627.8 kB (-0.0 kB) ⚪

No route changed by more than 1 kB. ✅

First load = polyfills + shared _app chunks + route chunks, gzipped.

@codecov

codecov Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.01916% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 55.8%. Comparing base (273327f) to head (7c5e0c6).

Files with missing lines Patch % Lines
src/middlewares/cidr.ts 91.2% 4 Missing and 4 partials ⚠️
src/pages/api/isBot.ts 92.2% 2 Missing and 1 partial ⚠️
src/middlewares/botCheckSignature.ts 91.7% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##           master    #949     +/-   ##
========================================
+ Coverage    55.1%   55.8%   +0.7%     
========================================
  Files         374     381      +7     
  Lines       11462   11729    +267     
  Branches     2514    2575     +61     
========================================
+ Hits         6307    6535    +228     
- Misses       4524    4559     +35     
- Partials      631     635      +4     
Files with missing lines Coverage Δ
src/middlewares/botCheck.ts 100.0% <100.0%> (ø)
src/middlewares/clientIp.ts 100.0% <100.0%> (ø)
src/middlewares/crawlers.ts 100.0% <100.0%> (ø)
src/middlewares/initSession.ts 84.5% <100.0%> (+2.4%) ⬆️
src/middlewares/reverseDns.ts 100.0% <100.0%> (ø)
src/ssr-utils.ts 86.4% <100.0%> (+2.7%) ⬆️
src/middlewares/botCheckSignature.ts 91.7% <91.7%> (ø)
src/pages/api/isBot.ts 92.7% <92.2%> (ø)
src/middlewares/cidr.ts 91.2% <91.2%> (ø)

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

/api/isBot sits outside the middleware matcher, so it was reachable
unauthenticated and would drive up to six resolver lookups per request on
a caller-supplied IP. The hop is now signed with an HMAC over the
timestamp, IP and user agent, keyed on COOKIE_SECRET, and the handler
rejects anything unsigned, stale or altered before touching DNS. The
response status is checked too, so a rejected hop no longer reads a 403
body as a HUMAN verdict.

A successful bootstrap also clears session.bot. Without it a session that
was once classified as a bot kept the flag after its token expired and
was replaced by a human one, and isUserIdentifiedAsBot then skipped the
apiCookieHash comparison for the rest of that token's life.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant