fix(website): Address PR review feedback on isBot pre-mint guard

Four items from gemini and claude reviews:

- botDetect.ts: Memoize isBot() result. navigator.userAgent is immutable
  for the page lifetime, so re-running the isbot regex on every Turnstile
  pre-mint debounce and post-submit re-mint check is wasted work. SSR
  fallback is intentionally not cached so a module instance reused across
  SSR/CSR still reaches the real UA check on first CSR call.
- usePackRequest.ts: Disambiguate the "submit-path NOT gated" comment —
  it was confusing because the new post-submit re-mint also lives inside
  submitRequest's finally. Reworded to "click-path acquireTurnstileToken"
  to make clear which call site is intentionally skipped.
- usePackRequest.ts: Update the userTouched comment to reflect autofill
  reality — modern Chromium/Firefox DO fire input events on autofill, so
  the rationale ("autofill doesn't trigger") was already stale. The new
  isBot() guard covers the gap for well-behaved crawler UAs.
- usePackRequest.ts: Add English glosses for the Japanese CF dashboard
  labels (提示チャレンジ / 未解決) so non-Japanese-reading maintainers can
  follow the comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kazuki Yamada
2026-05-09 20:07:08 +09:00
parent d94475ef01
commit e4a635c2f2
2 changed files with 32 additions and 11 deletions
+19 -10
View File
@@ -32,10 +32,16 @@ export function usePackRequest() {
const uploadedFile = ref<File | null>(null);
// True once the user has signalled real intent: typed/pasted a URL,
// uploaded a file/folder, switched modes, or tweaked options. Used to
// gate the Turnstile pre-mint so URL-parameter hydration (`?repo=...`),
// browser autofill, form restoration, and JS-executing link unfurlers
// don't trigger background challenges. Set-only — once true, it stays
// true for the session.
// gate the Turnstile pre-mint so URL-parameter hydration (`?repo=...`)
// and form restoration don't trigger background challenges. Set-only —
// once true, it stays true for the session.
//
// Caveat: modern Chromium / Firefox DO fire `input` events on browser
// autofill, which would flip this flag through TryItUrlInput's handler
// and trigger a wasted pre-mint for JS-executing crawlers that have
// autofill-like behaviour. The `isBot()` check at the pre-mint trigger
// sites covers that gap for well-behaved crawler UAs; sophisticated
// bots that spoof UA still get filtered server-side by siteverify.
const userTouched = ref(false);
// Request states
@@ -93,12 +99,15 @@ export function usePackRequest() {
// Skip background pre-mint for known crawlers. These visitors can't
// solve the Turnstile challenge anyway (the JS challenge requires
// real browser fingerprints), so issuing one only inflates the CF
// dashboard "提示チャレンジ" / "未解決" counters without producing a
// usable token. The actual security gate is the server-side
// siteverify in turnstileMiddleware — that stays unchanged, so a
// crawler that spoofs UA past `isBot()` still gets blocked there.
// Submit-path takeToken is intentionally NOT gated to avoid
// false-positive lockouts of legit users with unusual UAs.
// dashboard "提示チャレンジ" (issued challenges) / "未解決"
// (unsolved) counters without producing a usable token. The actual
// security gate is the server-side siteverify in
// turnstileMiddleware — that stays unchanged, so a crawler that
// spoofs UA past `isBot()` still gets blocked there. The click-path
// `acquireTurnstileToken()` (cold-mint at submit time) is
// intentionally NOT gated to avoid false-positive lockouts of legit
// users with unusual UAs; only the warm-up paths short-circuit here
// and at the post-submit re-mint below.
if (isBot()) return;
turnstile.preMintToken().catch(() => {
/* errors surface on the actual submit path */
+13 -1
View File
@@ -1,5 +1,14 @@
import { isbot } from 'isbot';
// Cache the per-session result. `navigator.userAgent` is immutable for the
// life of the page, and `isbot()` runs a non-trivial regex match — caching
// avoids re-parsing the UA on every Turnstile pre-mint debounce or
// post-submit re-mint check. The SSR fallback (`navigator === undefined`)
// is intentionally NOT cached so that if the same module instance is
// somehow reused between SSR and CSR, the CSR path still reaches the real
// UA check on first call.
let cached: boolean | undefined;
/**
* Detects whether the current user agent is a bot/crawler.
* Used to prevent automatic API calls when bots render pages with JavaScript.
@@ -8,5 +17,8 @@ export function isBot(): boolean {
if (typeof navigator === 'undefined') {
return false;
}
return isbot(navigator.userAgent);
if (cached === undefined) {
cached = isbot(navigator.userAgent);
}
return cached;
}