Skip to content

fix(api): rate-limit ingestion-quality and eval-cases admin routes (audit H2 remainder)#731

Merged
BigSimmo merged 2 commits into
mainfrom
claude/api-admin-rate-limits-part2
Jul 17, 2026
Merged

fix(api): rate-limit ingestion-quality and eval-cases admin routes (audit H2 remainder)#731
BigSimmo merged 2 commits into
mainfrom
claude/api-admin-rate-limits-part2

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

Completes Wave-H finding H2 (S6). PR #727 rate-limited the document-admin write routes; this covers the remaining two authenticated admin routes H2 named — ingestion/quality (GET dashboard) and eval-cases (POST capture) — which still had no per-owner rate-limit bucket.

  • Adds an ingestion_admin bucket (60/min per owner) in src/lib/api-rate-limit.ts.
  • Consults it before any table access in ingestion/quality (GET) and eval-cases (POST).
  • Exhaustion returns the shared 429 + Retry-After envelope via rateLimitJsonResponse.
  • Uses allowInMemoryFallbackOnUnavailable: true (matching the read/registry and document_admin buckets), so a durable-limiter outage degrades these authenticated routes to per-instance limiting rather than failing them closed.

With this, all six routes H2 named (bulk, labels, table-facts via #727; ingestion-quality, eval-cases here; jobs already hardened in H6) are rate-limited.

Verification

  • New tests/ingestion-admin-rate-limit.test.ts — 429 + Retry-After when the durable limiter reports the bucket exhausted, and no table access occurs.
  • 5 affected suites pass (29): ingestion-admin-rate-limit, eval-cases-route, ingestion-quality-route, public-access-deep, api-rate-limit-fallback. Added a not-limited rpc stub to the eval-cases and ingestion-quality mocks so their functional cases exercise the happy path through the new limiter check.
  • tsc, eslint, prettier clean on every changed file.
  • npm run verify:pr-local not run: the repo-wide typecheck fails only on pre-existing missing optional test dev deps (@testing-library/*, @axe-core/playwright) absent in this container and unrelated to this change.
  • npm run verify:ui — N/A; no UI, routing, or styling code changed (only src/app/api/** + src/lib).

Risk and rollout

  • Risk: low; adds a per-owner rate-limit ceiling to two existing authenticated owner-scoped admin routes. No change to auth, owner scoping, or data semantics; legitimate users stay well under 60/min, and a limiter outage degrades to per-instance limiting rather than open access.
  • Rollback: revert this PR's single commit. No migration, schema, or data change — the ingestion_admin bucket is application config only, so reverting fully removes the limiter with no cleanup or backfill.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes


Generated by Claude Code

…udit H2 remainder)

Completes Wave-H finding H2 (S6). The H2 PR (#727) rate-limited the document-admin
write routes; this covers the remaining two authenticated admin routes it named:
`ingestion/quality` (GET dashboard) and `eval-cases` (POST capture), which still had
no per-owner rate-limit bucket.

Adds an `ingestion_admin` bucket (60/min per owner) and consults it — before any
table access — in both routes; exhaustion returns the shared 429 + `Retry-After`
envelope via `rateLimitJsonResponse`. Like the document-admin bucket, it uses
`allowInMemoryFallbackOnUnavailable: true`, so a durable-limiter outage degrades
these authenticated routes to per-instance limiting rather than failing them closed.

Tests: new tests/ingestion-admin-rate-limit.test.ts proves a 429 + Retry-After when
the durable limiter reports the bucket exhausted (no table access occurs). Added a
not-limited rpc stub to tests/eval-cases-route.test.ts and
tests/ingestion-quality-route.test.ts so their functional cases exercise the happy
path through the new limiter check.

Verification: the 5 affected suites pass (29); api-rate-limit-fallback passes;
tsc/eslint/prettier clean on all changed files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zbpyexer9cLgxhrB61RCU
@supabase

supabase Bot commented Jul 17, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@BigSimmo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 428c1d9d-cdd4-4250-b959-152cbbd842ec

📥 Commits

Reviewing files that changed from the base of the PR and between 9db4436 and d1937d7.

📒 Files selected for processing (6)
  • src/app/api/eval-cases/route.ts
  • src/app/api/ingestion/quality/route.ts
  • src/lib/api-rate-limit.ts
  • tests/eval-cases-route.test.ts
  • tests/ingestion-admin-rate-limit.test.ts
  • tests/ingestion-quality-route.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/api-admin-rate-limits-part2

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo marked this pull request as ready for review July 17, 2026 17:55
@BigSimmo
BigSimmo enabled auto-merge July 17, 2026 17:56
@BigSimmo
BigSimmo merged commit d4f50c0 into main Jul 17, 2026
15 checks passed
@BigSimmo
BigSimmo deleted the claude/api-admin-rate-limits-part2 branch July 17, 2026 18:03
BigSimmo pushed a commit that referenced this pull request Jul 17, 2026
…mit buckets)

Resolves the expected conflict in src/lib/api-rate-limit.ts after #731 (the H2
remainder) merged first: #731 added the `ingestion_admin` bucket and this PR adds
`document_admin`. Keep-both resolution — both entries retained in the
ApiRateLimitBucket union and apiRateLimitDefaults. No behaviour change; the two
buckets are independent.

Verified: document-admin-rate-limit + api-rate-limit-fallback + document-mutation-routes
pass (19); tsc/eslint/prettier clean on api-rate-limit.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zbpyexer9cLgxhrB61RCU
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.

2 participants