fix(auth): fail open when the rate-limit Durable Object errors (#5000) - #5041
Conversation
enforceRateLimit runs as global middleware ahead of every route's own try/catch, and no app.onError is registered anywhere -- an uncaught Durable Object hiccup (eviction, migration, a rolling-deploy blip) escaped as Hono's bare, unstructured 500 for whatever route the caller happened to be hitting. Traced the 95 orb_broker_unavailable (500) events back to this middleware, not the /v1/orb/token handler itself: that handler and its DB-touching helpers were already hardened by the earlier #orb-broker-500 fix, but this shared rate-limit check ran ahead of it, unguarded, for every route. Fail open on both the DO check and its 429-denial audit write -- the rate limiter protects the app, it must not crash the request it's gating.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5041 +/- ##
=======================================
Coverage 94.28% 94.28%
=======================================
Files 461 461
Lines 39331 39335 +4
Branches 14351 14353 +2
=======================================
+ Hits 37083 37087 +4
Misses 1593 1593
Partials 655 655
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-11 10:06:59 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 7 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Summary
orb_broker_unavailable— 95 Sentry events over 10+ days, mixing "(500)", "(503)", and "The operation was aborted due to timeout" messages, still occurring after the earlier#orb-broker-500fix (commit65de78ec6, 2026-07-05) had already deployed.orb_broker_unavailableis not logged by the server hosting/v1/orb/token— it's logged client-side, inmintInstallationToken(src/github/app.ts:277-285), whenever a self-hosted box's own outbound call to the central broker (fetchBrokeredInstallationToken,src/orb/broker-client.ts:58-86) fails for any reason. That client function faithfully echoes whatever HTTP status (or network/timeout error) it receives —Orb broker token exchange failed (${response.status}).— so the "500 vs 503 vs timeout" mix in Sentry is just three different failure shapes reaching the client, not three different application bugs.app.post("/v1/orb/token", ...)insrc/api/routes.ts:3311-3337,brokerOrbTokeninsrc/orb/broker.ts,readOrbRelayRegisterBodyinsrc/orb/relay.ts): every throw path inside the route's own try/catch, andreadOrbRelayRegisterBodyitself, is already correctly hardened — confirming the issue's finding that re-wrapping this handler again would add nothing (satisfies requirement feat(scoring): add situational score projections #3: "don't just re-wrap the outer handler").enforceRateLimit(src/auth/rate-limit.ts:57), registered as global middleware (app.use("*", ...)inroutes.ts:904-909) ahead of every route's own error handling — including/v1/orb/token, classified"strict"(every call hits it). It calls theRATE_LIMITERDurable Object via.fetch()with no try/catch, and the app registers noapp.onErrorhandler anywhere (src/index.tsexports{ fetch: app.fetch, ... }unwrapped). A Durable Object hiccup — eviction, migration, a rolling-deploy blip, all real, intermittent, Cloudflare-side conditions — throws uncaught here, escapes the entire middleware chain, and Hono's default error handling returns a bare, unstructured500for whatever route the caller happened to be hitting. This is indistinguishable from an application bug in that specific route, but it isn't one — it's shared infrastructure sitting upstream of every route, not just the orb ones.rate_limit_check_failedand lets the request through (return null) instead of throwing — the rate limiter's job is to protect the app, not crash the request it's gating. Same treatment for the (rarer)rate_limit.deniedaudit write inside the 429 path: a failed audit write no longer prevents the 429 itself from reaching the caller.Requirement #1 (issue): which events are pre- vs post-fix?
Pulled the raw per-event timestamps rather than trusting the issue's summary. Events from 2026-06-29 through roughly 2026-07-05 predate
#orb-broker-500's fix, as expected. Events continuing through 2026-07-09 (after that fix shipped) are consistent with this middleware-layer gap, which#orb-broker-500never touched — it only hardenedreadOrbRelayRegisterBody. No evidence of a deploy-lag gap; the central Worker auto-deploys on merge (Cloudflare Workers Builds).Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #5000).Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocallynpm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateIf any required check was skipped, explain why:
test:coverage(full unsharded): not run end-to-end — ran scopedvitest --coveragefortest/unit/auth.test.ts(24 tests) plustest/integration/routes-errors.test.ts+test/integration/api.test.ts(85 tests combined) and confirmed via lcov that every changed line and branch (including both sides of theerror instanceof Errorternary in each new catch) is covered.actionlint/test:workers/build:mcp/test:mcp-pack/ui:openapi:check/ui:lint/ui:typecheck/ui:build/npm audit: not run — this change touches onlysrc/auth/rate-limit.ts(existing middleware, no new API/schema/binding/dependency surface) and its tests; no workflow, MCP, UI, or dependency-manifest surface changed.Safety
UI Evidencesection. (N/A.)Notes
Part of a batch of 13 bug fixes filed from a Sentry-issue triage this session (#4994–#5006). This is #7 by priority. This middleware runs ahead of every route (not just orb endpoints) — the fix should reduce bare, unstructured 500s fleet-wide whenever the rate-limiter's Durable Object has a transient hiccup, not just for this one signal.